如何在Haskell中打破循环?

hug*_*omg 7 haskell control-flow

Pipes教程当前版本在以下一个示例中使用以下两个函数:

 stdout :: () -> Consumer String IO r
 stdout () = forever $ do
     str <- request ()
     lift $ putStrLn str

 stdin :: () -> Producer String IO ()
 stdin () = loop
   where
     loop = do
         eof <- lift $ IO.hIsEOF IO.stdin
         unless eof $ do
             str <- lift getLine
             respond str
             loop
Run Code Online (Sandbox Code Playgroud)

正如教程本身所述,由于需要检查输入的结束,P.stdin有点复杂.

有没有什么好方法可以重写P.stdin,不需要手动尾递归循环,并使用像P.stdout那样的高阶控制流组合器?在命令式语言中,我将使用结构化的while循环或break语句来执行相同的操作:

while(not IO.isEOF(IO.stdin) ){
    str <- getLine()
    respond(str)
}

forever(){
    if(IO.isEOF(IO.stdin) ){ break }
    str <- getLine()
    respond(str)
}
Run Code Online (Sandbox Code Playgroud)

Gab*_*lez 11

我更喜欢以下内容:

import Control.Monad
import Control.Monad.Trans.Either   

loop :: (Monad m) => EitherT e m a -> m e
loop = liftM (either id id) . runEitherT . forever

-- I'd prefer 'break', but that's in the Prelude
quit :: (Monad m) => e -> EitherT e m r
quit = left
Run Code Online (Sandbox Code Playgroud)

你这样使用它:

import Pipes
import qualified System.IO as IO

stdin :: () -> Producer String IO ()
stdin () = loop $ do
    eof <- lift $ lift $ IO.hIsEOF IO.stdin
    if eof
    then quit ()
    else do
        str <- lift $ lift getLine
        lift $ respond str
Run Code Online (Sandbox Code Playgroud)

请参阅此博客文章,我将解释此技术.

我不在教程中使用它的唯一原因是我认为它不太适合初学者.


Dan*_*her 9

看起来像是一份工作whileM_:

stdin () = whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) (lift getLine >>= respond)
Run Code Online (Sandbox Code Playgroud)

或者,使用与原始示例类似的do-notation:

stdin () =
    whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) $ do
        str <- lift getLine
        respond str
Run Code Online (Sandbox Code Playgroud)

monad-loops软件包还提供了whileM返回中间结果列表而不是忽略重复操作的结果以及其他有用的组合器.