我从Foldr Foldl Foldl'读到foldl'
因为严格属性而对长有限列表更有效。我知道它不适合无限列表。
因此,我只限制长期有限列表的比较。
concatMap
是使用foldr
实现的,这会让它变得懒惰。但是,根据文章,使用长有限列表将构建一个长的未减少的链。
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap f xs = build (\c n -> foldr (\x b -> foldr c b (f x)) n xs)
因此,我使用foldl'
来提出以下实现。
concatMap' :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap' f = reverse . foldl' (\acc x -> f x ++ acc) []
我已经构建了以下两个函数来测试性能。
lastA = last . concatMap (: []) $ [1..10000]
lastB = last . concatMap' (: []) $ [1..10000]
然而,我对结果感到震惊。
lastA:
(0.23 secs, 184,071,944 bytes)
(0.24 secs, 184,074,376 bytes)
(0.24 secs, 184,071,048 bytes)
(0.24 secs, 184,074,376 bytes)
(0.25 secs, 184,075,216 bytes)
lastB:
(0.81 secs, 224,075,080 bytes)
(0.76 secs, 224,074,504 bytes)
(0.78 secs, 224,072,888 bytes)
(0.84 secs, 224,073,736 bytes)
(0.79 secs, 224,074,064 bytes)
concatMap
在时间和记忆上都超过了我的concatMap'
。我想知道我在concatMap'
实施中犯了错误。
因此,我怀疑陈述foldl'
的善意的文章。
concatMap
中是否有任何黑魔法使其如此高效?
对于长有限列表,foldl'
是否更有效?
使用带有长有限列表的foldr
是否会构建一个长的未减少的链并影响性能?
答案 0 :(得分:6)
concatMap中是否有任何黑魔法使其如此高效?
不,不是真的。
对于长有限列表,
foldl'
是否更有效?
并非总是如此。这取决于折叠功能。
关键是,foldl
和foldl'
始终必须在生成输出之前扫描整个输入列表。相反,foldr
并非总是如此。
作为极端情况,请考虑
foldr (\x xs -> x) 0 [10..10000000]
立即求值为10
- 仅评估列表的第一个元素。减少类似于
foldr (\x xs -> x) 0 [10..10000000]
= foldr (\x xs -> x) 0 (10 : [11..10000000])
= (\x xs -> x) 10 (foldr (\x xs -> x) 0 [11..10000000])
= (\xs -> 10) (foldr (\x xs -> x) 0 [11..10000000])
= 10
并且由于懒惰而无法评估递归调用。
通常,在计算foldr f a xs
时,在评估f y ys
之前检查ys
是否能够构建输出的一部分非常重要。例如
foldr f [] xs
where f y ys = (2*y) : ys
在评估_ : _
和2*y
之前,会生成一个列表单元格ys
。这使其成为foldr
的绝佳候选人。
同样,我们可以定义
map f xs = foldr (\y ys -> f y : ys) [] xs
运行得很好。它从xs
消耗一个元素并输出第一个输出单元格。然后它消耗下一个元素,输出下一个元素,依此类推。在处理整个列表之前,使用foldl'
不会输出任何内容,从而使代码效率非常低。
相反,如果我们写了
sum xs = foldr (\y ys -> y+ys) 0 xs
然后在xs
的第一个元素被消耗之后我们不输出任何内容。
我们建立了一长串的thunk,浪费了大量的内存。
在这里,foldl'
将在恒定的空间中工作。
使用带有长有限列表的
foldr
是否会构建一个长的未减少的链并影响性能?
并非总是如此。它在很大程度上取决于调用者如何消耗输出。
作为一个拇指规则,如果输出是" atomic",意味着输出消费者只能观察到它的一部分(例如Bool, Int, ...
),那么它会更好使用foldl'
。如果输出是"组成"许多独立值(列表,树,...)可能foldr
是一个更好的选择,如果f
可以逐步产生其输出,在流媒体"方式。