我有这个简单的代码,可以无限期地读取字符串并打印出来。
main :: IO ()
main = getLine >>= putStrLn >> main
现在我想在getLine
电话后退出,如果该行是“退出”或“退出”。
我的尝试:
main :: IO ()
main = do
line <- getLine
if line == "exit" || line == "quit"
then return ()
else putStrLn line >> main
对我来说看起来不是惯用语。还有更好的方法吗?
答案 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