5

我有这样的代码:

foldl (\a x -> a ++ (f x)) [] list

一切似乎都很好,但是(f x) :: IO [String],并(++)给出了一个错误([String]!= IO [String])。

如何定义空 IO[String] 列表?我无法清洁(fx)。

4

3 回答 3

15

您在这里想要的组合器可能是

Prelude Control.Monad> :t foldM
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

和那个

foldM (\a x -> fmap (a++) (f x)) [] list

依次运行动作f x并累积[String]它们产生的 s。

于 2013-01-15T21:44:19.350 回答
4

在:

foldl (\a x -> a ++ (f x)) [] list

你正在尝试做a ++ f x但是f x :: IO [String],所以它不起作用。让我们看看我们是否可以弄清楚发生了什么。

您的意思是 forx来自您的list,因此您将f依次应用于列表的每个元素,并且它们每个都给您 a[String]并且您希望在进行过程中将它们连接在一起。

我们将使用mapM :: Monad m => (a -> m b) -> [a] -> m [b],它将一个函数映射到一个列表上,然后将 monad 操作组合成一个,将输出组合成一个列表。

concatMapM :: (a -> IO [String]) -> [a] -> IO [String]
concatMapM f list = fmap concat (mapM f list)

这里我习惯于fmap将纯函数concat应用于 IO 操作的结果。

你可以使用它来测试它

fileLines :: FilePath -> IO [String]
fileLines filename = fmap lines (readFile filename)

*Main> concatMapM fileLines ["test1.txt","test2.txt"]
["This is test1.txt","It has two lines.","This is test2.txt","It has two lines too."]

这种方式比尝试 foldl 更好,因为concatfoldl (++). 要自己检查一下,请比较以下速度:

*Main> writeFile "test3.txt" $ foldl (++) [] (map show [1..10000])
*Main> writeFile "test3.txt" $ foldr (++) [] (map show [1..10000])

问题是这样foldl做会(((a++b)++c)++d)++e遍历列表a四次-它首先在添加时遍历列表,b然后在添加时遍历列表,a然后在添加时遍历所有四个列表。补充道。这是一个很大的努力。bcabcde

foldr这样a ++ (b ++ (c ++ (d ++ e)))做会遍历每个列表一次 -d然后c然后b然后aconcat是 a foldr,这对于这个列表操作要好得多。

于 2013-01-16T03:14:47.983 回答
1

假设您有以下内容:

list :: [a]
f :: a -> IO b

我假设您想获得 type 的最终值IO [b]。如果你映射f过来list,你会得到一个类型的列表[IO b]。您可以使用以下方式将 IO 从内部“移动到外部” sequence :: Monad m => [m a] -> m [a]

sequence (map f list) :: IO [b]

可以这样想:在排序之前,您有一个用于生成 type 值的指令列表b。现在你有一条指令来制作b's 列表。

于 2013-01-15T21:53:01.650 回答