优化连续的地图/过滤/折叠调用

que*_*ker 5 language-agnostic optimization functional-programming higher-order-functions

假设我有一个很重要的列表,我想要执行多个地图,过滤和折叠/减少调用.为了清晰和表达,这应该通过传递给map/filter/fold的小lambda函数来完成.但是,据我所知,这些实际上每次遍历列表,在其上调用lambda(可能是内联的)并生成一个新列表.如果是这种情况,我可以编写for-each循环并将所有lambdas合并到其正文中.

我测量了一个简单的map/filter/reduce算法的执行时间以及Python中每个循环的相应命令,后者的速度提高了两倍多,就像我预期的那样,但我知道Python不是这方面最好的语言.

我的问题是:编译器是否有可能找出这些并以某种方式将它们合并为一个循环?有没有编译器这样做?我主要对函数式语言(Haskell,Erlang/Elixir,Scala)感兴趣,但也很高兴听到其他语言(Rust的实现,LINQ).

phi*_*ler 6

是的,这样的优化已经考虑过很多次了。

使用的一个术语或方法是“融合”(也称为流或图融合),其目标是智能地内联迭代转换,例如map f . map g = map (f . g). 这主要必须在编译器的帮助下完成,但可以在这些函数的“正常”实现上工作(如果它们做得有点智能的话)。

另一种方法是通过累积所有中间闭包来手动执行这种内联,并且仅在实际需要值时应用组合转换(这与惰性求值密切相关,这在某些语言(如 Haskell)中会完成)自动地)。这些东西可以在 Scala 的视图Streams 或 Clojure 的转换器(尽管其工作方式更复杂)中找到。这些懒惰的东西的问题是它们往往更容易遇到空间问题(我听说过)。

Python 中的迭代器(以及 C# 的IEnumerable/LINQ 内容和 Java 的新Streams)原理通过后一个原理工作,涉及语言提供的迭代支持(涉及一些内部状态)。这就是为什么xs = map(print, range(10))不会立即打印任何内容,而只能遍历一次;在迭代的每一步,嵌套迭代器都会互相询问下一个值,对其进行转换并更新其状态。(您测量到的差异可能更多是由于涉及的机制而不是重复迭代造成的。)