我有这样的代码:
foldl (\a x -> a ++ (f x)) [] list
似乎一切正常,但是(f x) :: IO [String]
和(++)
会出错([String]!= IO [String])。
如何定义空IO [String]列表?我不能干净(f x)。
答案 0 :(得分:12)
你想要的组合器可能是
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]
。
答案 1 :(得分:3)
在:
foldl (\a x -> a ++ (f x)) [] list
你正在尝试a ++ f x
而f x :: IO [String]
,所以它不起作用。让我们看看我们是否可以解决正在发生的事情。
您的意思是x
来自list
,因此您将依次将f
应用于列表中的每个元素,并且每个元素都会给您{ {1}}并且您希望在进行中将它们连接在一起。
我们将使用[String]
,它将一个函数映射到一个列表,然后将monad操作一起排序为一个,将输出组合成一个列表。
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
这里我使用concatMapM :: (a -> IO [String]) -> [a] -> IO [String]
concatMapM f list = fmap concat (mapM f list)
将纯函数fmap
应用于IO操作的结果。
您可以使用
进行测试concat
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."]
比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
时首先运行它,然后它会贯穿{ {1}}和b
添加a
,然后在添加b
时运行c
,a
和b
,然后当它添加c
时,会遍历所有这四个。这是一个很大的浪费。
d
执行e
因此会在每个列表中运行一次 - foldr
然后a ++ (b ++ (c ++ (d ++ e)))
然后d
然后c
。 b
是a
,对于此列表操作来说要好得多。
答案 2 :(得分:1)
假设您有以下内容:
list :: [a]
f :: a -> IO b
我假设您希望获得类型为IO [b]
的最终值。如果您将f
映射到list
,则会得到类型[IO b]
的列表。您可以使用sequence :: Monad m => [m a] -> m [a]
将IO从内部移动到外部,如下所示:
sequence (map f list) :: IO [b]
以这种方式思考:在排序之前,您有一个用于创建类型b
的值的指令列表。现在您只需要一条指令来制作b
的列表。