3

我有一个简单的列表,我想在每个元素之间迭代“yield”并将该元素打印到输出。我正在尝试使用 ContT monad 来执行此操作,但遇到了问题。这是我到目前为止所拥有的:

data K a = Nil | K (a,() -> K a)
listIterator :: (Monad m) => [r] -> m (K r)
listIterator [] = return Nil
listIterator (x:xs) = return (ContT (\k -> K (x,k))) >> listIterator xs

runIterator :: IO ()
runIterator = do
  a <- listIterator ([1,2,3] :: [Int])
  let loop Nil = liftIO $ print "nil"
      loop (K (curr,newI)) =
          do
        liftIO $ print curr
        loop (newI ())
  loop a

预期的输出是:

1
2
3
nil

我得到的是:

nil

任何帮助表示赞赏!

4

2 回答 2

4
listIterator (x:xs) = return (ContT (\k -> K (x,k))) >> listIterator xs

不符合你的预期,等式推理

listIterator (x:xs) 
= return (ContT (\k -> K (x,k))) >> listIterator xs
= (return (ContT (\k -> K (x,k)))) >>= \_ -> listIterator xs
= (\_ -> listIterator xs) (ContT (\k -> K (x,k)))
= listIterator xs

我不确定你为什么要使用迭代器。Haskell 已经很懒惰了,所以像这样的迭代模式主要只在您遇到需要与需求驱动的使用模式进行良好交互的资源管理问题时使用。而且,您根本不需要延续单子:

而不是编写K构造函数来获取一个元组,它更习惯于

data K a = Nil | K a (() -> K a)

直观地, 的类型listIterator不使用它的一元结构:它只是构造一个值,所以

listIterator ::[r] -> K r
listIterator [] = Nil
listIterator (x:xs) = K x (\_ -> listIterator xs)

现在生活微不足道

runIterator :: IO ()
runIterator = do
 let a = listIterator ([1,2,3] :: [Int])
     loop Nil = liftIO $ print "nil"
     loop (K curr newI) =
         do
       liftIO $ print curr
       loop (newI ())
 loop a

这可能最好在不使用 do 表示法的情况下编写。

于 2012-07-22T18:42:59.863 回答
1

这可能不是您要寻找的答案,但如果您对这种编程风格感兴趣,您应该研究pipes类似的库。(conduit是“现实世界”中的后起之秀,但管道提供了一种更简单的教学工具,这就是我在这里使用它的原因。)

$ cabal update && cabal install pipes

管道就像迭代器,除了它们有三种形式:可以获取输入的那些(消费者),产生输出的(生产者),以及两者都做的那些(管道)。如果你连接管道使得输入和输出端都满足,那么它被称为“管道”,它是一个独立的单元,可以在没有任何额外输入的情况下运行。

Pipe 提供了一个 monad 实例以便于创建管道。操作员将>+>两条管道连接在一起。

import Control.Pipe
import Control.Monad.Trans.Class
import Control.Monad.IO.Class

-- annoyingly, Pipe does not provide a MonadIO instance
instance (MonadIO m) => MonadIO (Pipe a b m) where
  liftIO = lift . liftIO

listIterator :: Monad m => [a] -> Producer (Maybe a) m ()
listIterator (x:xs) = yield (Just x) >> listIterator xs
listIterator []     = yield Nothing

printer :: (MonadIO m, Show a) => Consumer (Maybe a) m ()
printer = do
  mx <- await
  case mx of
    Just x -> liftIO (print x) >> printer
    Nothing -> liftIO (putStrLn "nil")

main = runPipe $ listIterator [1, 2, 3] >+> printer

Control.Pipe的源代码非常简单,特别是如果您一直在阅读 Gabriel 最近关于 Free monads 的博客文章,特别是Why free monads matterPurify code using free monads

于 2012-07-22T23:53:08.217 回答