我有这样的代码:
foldl (\a x -> a ++ (f x)) [] list
一切似乎都很好,但是(f x) :: IO [String]
,并(++)
给出了一个错误([String]!= IO [String])。
如何定义空 IO[String] 列表?我无法清洁(fx)。
您在这里想要的组合器可能是
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。
在:
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 更好,因为concat
比foldl (++)
. 要自己检查一下,请比较以下速度:
*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
然后在添加时遍历所有四个列表。补充道。这是一个很大的努力。b
c
a
b
c
d
e
foldr
这样a ++ (b ++ (c ++ (d ++ e)))
做会遍历每个列表一次 -d
然后c
然后b
然后a
。concat
是 a foldr
,这对于这个列表操作要好得多。
假设您有以下内容:
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 列表。