Haskell:优雅地循环用户输入

Haskell: looping over user input gracefully

这是来自 Learn You a Haskell 的示例:

main = do
    putStrLn "hello, what's your name?"
    name <- getLine
    putStrLn ("Hey, " ++ name ++ ", you rock!")

为清楚起见,没有 do 的相同重做:

main =
    putStrLn "hello, what's your name?" >>
    getLine >>= \name ->
    putStrLn $ "Hey, " ++ name ++ ", you rock!"

我应该如何干净地循环它(直到 "q"),Haskell 方式(不鼓励使用 do)?

我从Haskell - loop over user input

那里借来的
main = mapM_ process . takeWhile (/= "q") . lines =<< getLine
    where process line = do
                            putStrLn line

初学者,但不会循环。

您可以再次调用 main 并检查您的字符串是否为 "q"。

import Control.Monad

main :: IO ()
main =
    putStrLn "hello, what's your name?" >>
    getLine >>= \name ->
                  when (name /= "q") $ (putStrLn $ "Hey, " ++ name ++ ", you rock!") >> main

λ> main
hello, what's your name?
Mukesh Tiwari
Hey, Mukesh Tiwari, you rock!
hello, what's your name?
Alexey Orlov
Hey, Alexey Orlov, you rock!
hello, what's your name?
q
λ> 

也许你也可以通过调整 System.IO.Lazy 包来在 IO 类型上使用惰性。它基本上只包含 run :: T a -> IO ainterleave :: IO a -> T a 函数来回将 IO 动作转换为惰性动作。

import qualified System.IO.Lazy as LIO

getLineUntil :: String -> IO [String]
getLineUntil s = LIO.run ((sequence . repeat $ LIO.interleave getLine) >>= return . takeWhile (/=s))

printData :: IO [String] -> IO ()
printData d = d >>= print . sum . map (read :: String -> Int)

*Main> printData $ getLineUntil "q"
1
2
3
4
5
6
7
8
9
q
45

在上面的代码中,我们通过 repeat $ LIO.interleave getLine 类型 [T String] 构造了一个惰性 getLine 的无限列表,并通过 sequence 将其转换为 T [String] 输入并继续阅读,直到收到 "q"。 printData 效用函数正在汇总并打印输入的整数。