【发布时间】:2017-03-26 18:50:49
【问题描述】:
我从Foldr Foldl Foldl' 了解到,foldl' 对于长有限列表更有效,因为它具有严格性属性。我知道它不适合无限列表。
因此,我只限制长有限列表的比较。
concatMap
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与长有限列表一起使用会建立一个长的未归约链并影响性能?
【问题讨论】:
-
好吧,你使用
++,它在 O(n) 中运行,其中 n 是左列表的大小......所以你通常想要避免这种情况(因为你要在一个 concat 上定义一个 concat ......) -
@Willem 感谢您的及时回复。对于
concatMap,我怎样才能避免++?此外,f x对于测试用例的大小为 1。 -
foldl'很好,如果您要使用严格的函数从种子构建一个很好的严格数据类型的东西来更新 - 比如以Int种子开始使用(+)的Int举个最简单的例子。如果你是从列表中构造列表或其他惰性结构,foldr会更自然。 -
注意你的函数也不正确——你应该反转
f x(试试concatMap (\x -> [-x,x]) [1,2,3]和concatMap' (\x -> [-x,x]) [1,2,3])
标签: performance haskell functional-programming fold