Haskell如何将[IO String]连接为IO String?

4

我的目标是编写一个 Haskell 函数,从输入中读取 N 行,并将它们连接成一个字符串。以下是第一次尝试:

readNLines :: Int -> IO String
readNLines n = do
  let rows = replicate n getLine
  let rowsAsString = foldl ++ [] rows 
  return rowsAsString  

以下是有关foldl的Haskell投诉:

无法匹配预期类型[a]和推断类型(a1 -> b -> a1) -> a1 -> [b] -> a1'

据我所知,行的类型是[IO String],是否可能以某种方式将这样的列表连接为单个IO String

5个回答


6
除了 ephemient 指出的问题,我认为你还有一个语法问题:你使用 ++ 运算符的方式让它看起来像你在尝试用 foldl 和 [] 作为操作数调用 ++ 运算符。将 ++ 运算符放入括号中以清晰表明你的意图。
foldl (++) [] rows

啊,是的,那就是类型检查错误的直接原因……我忽略了它,因为即使在 OP 修复了它之后,他们仍然有另一个问题。 - ephemient
还应该注意到,foldl(++)[]concat是相同的。 - HaskellElephant

5
你需要的功能是sequence,但应该注意的是:
sequence (replicate n f)

相同。
replicateM n f

foldl (++) []等价于concat。因此,你的函数应该是:

readNLines n = liftM concat (replicateM n getLine)

如果您希望保留换行符:

readNLines n = liftM unlines (replicateM n getLine)

"concat" 是 "foldr (++) []",而不是 "foldl":在无限流的列表上可以正常工作。你在后面的例子中也缺少了 "n" 作为 "replicateM" 的参数。但是,是的,这些都是很好的观点。 - ephemient
@ephemient,感谢您的提醒,我已经修复了n的错误。是的,concat实际上就是foldr(++)[],我的意思是他写的是foldl(++)[],这与concat等效,尽管正如您所提到的,concat在处理无限流时更好用。 - HaskellElephant

1
我能想到的最简短的答案是:
import Control.Applicative
import Control.Monad

readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine

0

replicate 返回一个 IO String 操作列表。为了执行这些操作,它们需要在 IO monad 中运行。因此,您不想加入 IO 操作数组,而是按顺序运行它们并返回结果。

这是我会做的事情

readNLines :: Int -> IO String
readNLines n = do
  lines <- replicateM n getLine
  return $ concat lines

或者,以应用风格:

import Control.Applicative

readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine

这两个都使用单子复制(replicateM),它按顺序评估一系列单子值,而不仅仅返回操作列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接