我很好奇是否有任何(仅限一阶多态)优化折叠。
对于地图,有森林砍伐:map g (map f ls) => map (g . f) ls
和rev (map f ls) => rev_map f ls
(Ocaml速度更快)。
但折叠是如此强大,似乎无视任何优化。
答案 0 :(得分:4)
明显的:
fold_left f acc (List.map g li) => fold_left (fun acc x -> f acc (g x)) acc li
fold_right f li acc => fold_left f acc li (* if (f,acc) is a monoid *)
您可能对有关“香蕉,镜头,信封和铁丝网的功能编程”主题的经典论文感兴趣。但要注意它是技术性的并且具有难以理解的符号。
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125
编辑:我的第一个规则的第一个版本错了,感谢vincent-hugot编辑。
答案 1 :(得分:3)
您可以在折叠处使用砍伐森林。事实上,map/map
融合是一个特例。
诀窍是用特殊的build
函数替换列表构造:
build :: (forall b. (a -> b -> b) -> b -> b) -> [a]
build g = g (:) []
现在,使用foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr c n [] = n
foldr c n (x:xs) = c x (foldr c n xs)
我们有以下等价物:
foldr c n (build g) == g c n
(实际上,只有在某些情况下,这是常见的情况。有关详细信息,请参阅"Correctness of short-cut fusion")。
如果使用map
和使用build
的消费者编写列表生成函数(包括foldr
),则上述等式可以删除大多数中间列表。 Haskell的列表推导被翻译为build
和foldr
的组合。
这种方法的缺点是它无法处理左侧折叠。 Stream Fusion处理这个问题就好了。它将列表生成器和转换器表示为流(coinductive数据类型,类似于迭代器)。上面的论文非常易读,所以我建议你看一下。
gasche提到的“香蕉”论文详细介绍了各种折叠及其等价物。
最后,还有Bird和Moor的"Algebra of Programming",其中提到了诸如combining two folds into one之类的转换。
答案 2 :(得分:1)
如果您对理论有所了解,我建议您阅读有关catamorphisms,anamorphisms和hylomorphisms的内容。虽然围绕它的类别理论可能看起来有点可怕,但这个概念并不那么困难。
Catamorphisms是使用递归数据结构并产生某种值的函数。 Anamorphisms是给出一些值(一种种子)产生递归数据结构的函数。特别是,其他论文中提到的foldr
和build
是在列表上构建catamorphisms和anamorphisms的函数。但是这个概念基本上可以应用于任何递归数据结构,例如不同种类的树等。
现在,如果你构建一个带有变形的递归数据结构然后用一个变形消耗它,你会得到所谓的hylomorphism。在这种情况下,您实际上不需要中间结构。您可以跳过创建并销毁它。这通常称为deforestation。
关于map
:这个函数很有意思,它既是一个变形又是一个变形:
map
使用一个列表并生成一些东西;还map
生成一个列表,消耗了一些东西。因此,您可以将两个地图map f . map g
的组合视为具有变形(map g
)的变形(map f
)的组合,形成一个同性异形。所以你知道可以通过不创建中间列表来优化(砍伐森林)。
具体一点:您可以用两种方式编写map
,一种使用foldr
,另一种使用build
:
mapAna :: (a -> b) -> [a] -> [b]
mapAna f xs = build (mapAna' f xs)
mapAna' :: (a -> b) -> [a] -> (b -> c -> c) -> c -> c
mapAna' f [] cons nil = nil
mapAna' f (x : xs) cons nil = (f x) `cons` (mapAna' f xs cons nil)
mapCata :: (a -> b) -> [a] -> [b]
mapCata f xs = foldr (\x ys -> f x : ys) [] xs
和作品map f (map g zs)
为mapCata f (mapAna g zs)
,经过一些简化并应用foldr c n (build g) == g c n
会产生map (f . g)
。