13

我在haskell中有一个程序,它必须从用户那里读取任意行的输入,当用户完成时,必须将累积的输入发送到一个函数。

在命令式编程语言中,这看起来像这样:

content = ''
while True:
    line = readLine()
    if line == 'q':
        break
    content += line
func(content)

我发现这在haskell中很难做到,所以我想知道是否有haskell等价物。

4

4 回答 4

20

与迭代等效的 Haskell 是递归。IO如果您必须阅读输入行,您还需要在 monad 中工作。大体图片是:

import Control.Monad

main = do
  line <- getLine
  unless (line == "q") $ do
    -- process line
    main

如果您只想在 中累积所有读取行content,则不必这样做。只需使用getContentswhich 将检索(懒惰地)所有用户输入。刚看到就停下来'q'。在非常惯用的 Haskell 中,所有读取都可以在一行代码中完成:

main = mapM_ process . takeWhile (/= "q") . lines =<< getContents
  where process line = do -- whatever you like, e.g.
                          putStrLn line

如果你从右到左阅读第一行代码,它会说:

  1. 获取用户将提供的所有内容作为输入(不要害怕,这是懒惰的);

  2. 将它分成几行;

  3. 只取不等于“q”的线,看到这样的线就停下来;

  4. 并呼叫process每一行。

如果您还没有弄清楚,您需要仔细阅读 Haskell 教程!

于 2013-09-02T13:07:05.323 回答
8

在 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

这实际上是一个 IO 动作的定义,它返回一个String. <-由于它是一个 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
于 2013-09-02T13:15:26.580 回答
2

Using 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)

这将使输入流式传输,并且永远不会将超过一行加载到内存中。

于 2013-09-02T14:47:12.553 回答
1

你可以做类似的事情

import Control.Applicative ((<$>))

input <- unlines . takeWhile (/= "q") . lines <$> getContents

然后输入将是用户写的内容,直到(但不包括)q。

于 2013-09-02T13:53:47.690 回答