我在haskell中有一个程序,它必须从用户读取任意输入行,当用户完成时,必须将累积的输入发送到函数。
在命令式编程语言中,这将是这样的:
content = ''
while True:
line = readLine()
if line == 'q':
break
content += line
func(content)
我发现在haskell中这非常难以做到,所以我想知道是否有相似的haskell。
答案 0 :(得分:16)
相当于迭代的Haskell是递归。如果你必须阅读输入行,你还需要在IO
monad中工作。总体情况是:
import Control.Monad
main = do
line <- getLine
unless (line == "q") $ do
-- process line
main
如果您只想累积content
中的所有读取行,则不必这样做。只需使用getContents
,它将检索(懒惰)所有用户输入。当你看到'q'
时就停止。在非常惯用的Haskell中,所有阅读都可以在一行代码中完成:
main = mapM_ process . takeWhile (/= "q") . lines =<< getContents
where process line = do -- whatever you like, e.g.
putStrLn line
如果你从右到左阅读第一行代码 ,它会说:
获取用户将提供的所有内容作为输入(从不担心,这是懒惰的);
将它分成行;
只取线,当你看到这样的线时停止;
并为每一行致电process
。
如果您还没有弄清楚,那么您需要仔细阅读Haskell教程!
答案 1 :(得分:7)
在Haskell中相当简单。最棘手的部分是你想要累积用户输入的序列。在命令式语言中,您使用循环来执行此操作,而在Haskell中,规范方法是使用递归辅助函数。它看起来像这样:
getUserLines :: IO String -- optional type signature
getUserLines = go ""
where go contents = do
line <- getLine
if line == "q"
then return contents
else go (contents ++ line ++ "\n") -- add a newline
这实际上是一个返回String
的IO操作的定义。由于它是IO操作,因此使用<-
语法而不是=
赋值语法访问返回的字符串。如果您想快速浏览一下,我建议您阅读The IO Monad For People Who Simply Don't Care。
您可以在GHCI提示符下使用此功能,如此
>>> str <- getUserLines
Hello<Enter> -- user input
World<Enter> -- user input
q<Enter> -- user input
>>> putStrLn str
Hello -- program output
World -- program output
答案 2 :(得分:2)
使用本周末即将发布的pipes-4.0
:
import Pipes
import qualified Pipes.Prelude as P
f :: [String] -> IO ()
f = ??
main = do
contents <- P.toListM (P.stdinLn >-> P.takeWhile (/= "q"))
f contents
将所有行加载到内存中。但是,您也可以在生成时处理每一行:
f :: String -> IO ()
main = runEffect $
for (P.stdinLn >-> P.takeWhile (/= "q")) $ \str -> do
lift (f str)
这会传输输入,永远不会将多行添加到内存中。
答案 3 :(得分:1)
您可以执行类似
的操作import Control.Applicative ((<$>))
input <- unlines . takeWhile (/= "q") . lines <$> getContents
然后输入将是用户写的,直到(但不包括)q。