在Haskell中有条件地“返回()”在monad链(>>,>> =)中的方法更短?

时间:2013-08-17 16:06:33

标签: haskell

我有这个简单的代码,可以无限期地读取字符串并打印出来。

main :: IO ()
main = getLine >>= putStrLn >> main

现在我想在getLine电话后退出,如果该行是“退出”或“退出”。

我的尝试:

main :: IO ()
main = do
  line <- getLine
  if line == "exit" || line == "quit"
  then return ()
  else putStrLn line >> main

对我来说看起来不是惯用语。还有更好的方法吗?

5 个答案:

答案 0 :(得分:16)

Control.Monad.unless(这是一个稍微受欢迎的堂兄,when)从你的代码中抽象出这种模式:

import Control.Monad (unless)

main = do
  line <- getLine
  unless (line == "exit" || line == "quit") $ do
    putStrLn line
    main
  -- or
  when (line /= "exit" && line /= "quit") $ do
    putStrLn line
    main

条件return ()后跟无条件代码将无法解决问题,因为return只是一个函数,而不是大多数其他语言中的流控制关键字。

答案 1 :(得分:9)

使用pipes-4.0

import Pipes
import qualified Pipes.Prelude as P

main = runEffect $
    P.stdinLn >-> P.takeWhile (`notElem` ["quit", "exit"]) >-> P.stdoutLn

答案 2 :(得分:4)

由于使用if / else和do符号,您似乎关心代码的顺序感觉。您可以尝试以下方式:

main = getLine >>= proc
  where
    proc s | s == "exit" || s == "quit" = return ()
           | otherwise = putStrLn s >> main

答案 3 :(得分:3)

尝试时尚:

module Main where

import Control.Monad
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class
import System.IO

isValid s = s ≠ "quit" && s ≠ "exit"

getL ∷ MaybeT IO String
getL = do s ← lift getLine
          guard (isValid s)
          return s


main = runMaybeT main' where
  main' = do
      lift $ putStr "Enter line: "
      lift $ hFlush stdout
      s ← getL
      lift $ putStrLn $  "Your line is: " ⧺ s
      main'

答案 4 :(得分:2)

我们可以创建一个辅助函数,在返回值时重复给定的操作:

import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Maybe

while :: (Monad m) => MaybeT m b -> m ()
while k = runMaybeT (forever k) >> return ()

k返回mzero后,循环停止。然后我们可以很好地使用它来使用标准MonadPlus组合器在任何地方中断循环:

main = while $ do
        l <- lift getLine
        guard $ l /= "quit"
        lift $ putStrLn l

或者在一条线上:

main = while $ mfilter (/= "quit") (lift getLine) >>= lift . putStrLn

更新:也许最简单的解决方案是使用 monad-loops 中的whileJust_

isValid s | s /= "quit"   = Just s
          | otherwise     = Nothing

main = whileJust_ (isValid `liftM` getLine) putStrLn