折叠优化

时间:2011-01-31 13:43:52

标签: performance functional-programming ocaml fold

我很好奇是否有任何(仅限一阶多态)优化折叠。

对于地图,有森林砍伐:map g (map f ls) => map (g . f) lsrev (map f ls) => rev_map f ls(Ocaml速度更快)。

但折叠是如此强大,似乎无视任何优化。

3 个答案:

答案 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的列表推导被翻译为buildfoldr的组合。

这种方法的缺点是它无法处理左侧折叠。 Stream Fusion处理这个问题就好了。它将列表生成器和转换器表示为流(coinductive数据类型,类似于迭代器)。上面的论文非常易读,所以我建议你看一下。

gasche提到的“香蕉”论文详细介绍了各种折叠及其等价物。

最后,还有Bird和Moor的"Algebra of Programming",其中提到了诸如combining two folds into one之类的转换。

答案 2 :(得分:1)

如果您对理论有所了解,我建议您阅读有关catamorphismsanamorphismshylomorphisms的内容。虽然围绕它的类别理论可能看起来有点可怕,但这个概念并不那么困难。

Catamorphisms是使用递归数据结构并产生某种值的函数。 Anamorphisms是给出一些值(一种种子)产生递归数据结构的函数。特别是,其他论文中提到的foldrbuild是在列表上构建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)