函数式语言编译器是否将列表上的 "filter, then map" 操作优化为单次传递?
Do functional language compilers optimize a "filter, then map" operation on lists into a single pass?
我在工作日主要使用 TypeScript,在应用函数模式时,我经常看到类似这样的模式:
const someArray = anotherArray.filter(filterFn).map(transformFn)
此代码将过滤 anotherArray
的所有项目,然后再次遍历过滤后的列表(如果没有过滤任何项目,则可能是相同的)并映射事物。换句话说,我们遍历数组两次。
这种行为可以通过使用 reduce
:
在阵列上“单次传递”来实现
const someArray = anotherArray.reduce((acc, item) => {
if (filterFn(item) === false) {
return acc;
}
acc.push(item);
return acc;
}, [])
我想知道这种优化是否是转译器(在 TS 世界中)知道自动执行的事情,以及这种优化是否在更多“功能优先”的语言中自动完成,例如 Clojure 或 Haskell。例如,我知道函数式语言通常使用尾递归进行优化,所以我也想知道“过滤然后映射”的情况。这是编译器实际做的事情吗?
首先,您通常不应该着迷于将所有内容都放在一个通道中。在小型容器上,运行 两次单操作循环和一次 运行 双操作循环之间没有太大区别。旨在编写易于理解的代码。一个 for 循环可能比两个循环更具可读性,但 reduce 并不比 filter 和 map 更具可读性。
编译器做什么取决于您的“容器”。当您的循环大到足以关心执行时间时,它通常也大到足以关心内存消耗。因此,在处理下一个元素之前,过滤然后映射到 observable 之类的东西,一次只处理一个元素,一直通过管道。这意味着您只需要一个元素的内存,即使您的 observable 可能是无限的。
我在工作日主要使用 TypeScript,在应用函数模式时,我经常看到类似这样的模式:
const someArray = anotherArray.filter(filterFn).map(transformFn)
此代码将过滤 anotherArray
的所有项目,然后再次遍历过滤后的列表(如果没有过滤任何项目,则可能是相同的)并映射事物。换句话说,我们遍历数组两次。
这种行为可以通过使用 reduce
:
const someArray = anotherArray.reduce((acc, item) => {
if (filterFn(item) === false) {
return acc;
}
acc.push(item);
return acc;
}, [])
我想知道这种优化是否是转译器(在 TS 世界中)知道自动执行的事情,以及这种优化是否在更多“功能优先”的语言中自动完成,例如 Clojure 或 Haskell。例如,我知道函数式语言通常使用尾递归进行优化,所以我也想知道“过滤然后映射”的情况。这是编译器实际做的事情吗?
首先,您通常不应该着迷于将所有内容都放在一个通道中。在小型容器上,运行 两次单操作循环和一次 运行 双操作循环之间没有太大区别。旨在编写易于理解的代码。一个 for 循环可能比两个循环更具可读性,但 reduce 并不比 filter 和 map 更具可读性。
编译器做什么取决于您的“容器”。当您的循环大到足以关心执行时间时,它通常也大到足以关心内存消耗。因此,在处理下一个元素之前,过滤然后映射到 observable 之类的东西,一次只处理一个元素,一直通过管道。这意味着您只需要一个元素的内存,即使您的 observable 可能是无限的。