如何在Haskell中打破循环?

时间:2013-06-23 19:16:52

标签: haskell control-flow

current version of the Pipes tutorial在以下一个示例中使用以下两个函数:

 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

正如本教程本身所述,由于需要检查输入结束,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)
}

4 个答案:

答案 0 :(得分: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

你这样使用它:

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

请参阅this blog post我在哪里解释这种技术。

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

答案 1 :(得分:9)

看起来像whileM_的作业:

stdin () = whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) (lift getLine >>= respond)

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

stdin () =
    whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) $ do
        str <- lift getLine
        respond str

monad-loops包还提供whileM,它返回一个中间结果列表,而不是忽略重复操作的结果,以及其他有用的组合器。

答案 2 :(得分:1)

由于没有隐式流,所以没有像“break”这样的东西。此外,您的样本已经是小块​​,将用于更复杂的代码。

如果你想停止“生成字符串”,你的抽象应该支持它。即使用Consumer中的特殊monad和/或与此相关的其他monad中的“管道”的一些“管理”。

答案 3 :(得分:0)

您只需导入System.Exit ,然后使用 exitWith ExitSuccess

EG。 if(输入=='q')        然后退出ExitSuccess        否则打印5(任何东西)