折叠/减少功能语言的实际使用
-
26-10-2019 - |
题
Fold
(又名 reduce
)被认为是非常重要的高阶功能。 Map
可以用 fold
(看这里)。但这听起来比我实用。典型的用途可能是获取数字的总和,产品或最大数字,但是这些功能通常接受任何数量的参数。那为什么写 (fold + 0 '(2 3 5))
什么时候 (+ 2 3 5)
工作正常。我的问题是,在哪种情况下,使用最容易或最自然 fold
?
解决方案
重点 fold
是更抽象。这并不是说您可以做以前做不到的事情,而是您可以更轻松地做它们。
用一个 fold
, ,您可以概括 任何 在两个元素上定义的函数适用于任意数量的元素。这是一场胜利,因为它通常比列表更容易编写,测试,维护和修改一个应用两个参数的单个函数。而且它总是更容易编写,测试,维护等。一个简单的功能,而不是具有相似但不符合功能的两个功能。
自从 fold
(为此, map
, filter
, ,朋友)具有明确的行为,使用这些功能比显式递归更容易理解代码。
基本上,一旦拥有一个版本,您就可以免费获得另一个版本。最终,您最终做的工作更少以获得相同的结果。
其他提示
这里有一些简单的示例 reduce
效果很好。
找到每个子列表的最大值的总和
Clojure:
user=> (def x '((1 2 3) (4 5) (0 9 1)))
#'user/x
user=> (reduce #(+ %1 (apply max %2)) 0 x)
17
球拍:
> (define x '((1 2 3) (4 5) (0 9 1)))
> (foldl (lambda (a b) (+ b (apply max a))) 0 x)
17
从列表构建地图
Clojure:
user=> (def y '(("dog" "bark") ("cat" "meow") ("pig" "oink")))
#'user/y
user=> (def z (reduce #(assoc %1 (first %2) (second %2)) {} y))
#'user/z
user=> (z "pig")
"oink"
对于一个更复杂的Clojure示例 reduce
, , 查看 我的解决方案 提出欧拉问题 18 & 67.
也可以看看: 减少申请
在共同的LISP函数中,不接受任何数量的参数。
每个常见的LISP实施中都有一个不变的定义 呼叫arguments-limit, ,必须是50或更大。
这意味着任何此类便携式书面功能都应至少接受50个参数。但这可能只有50。
存在此限制,以允许编译器可能使用优化的调用方案,并且不提供一般情况,如果可以通过无限数量的参数。
因此,要在便携式公共LISP代码中真正处理大型(大于50个元素)或向量,则有必要使用迭代构造,简化,映射和类似的方法。因此,也有必要不使用 (apply '+ large-list)
但是使用 (reduce '+ large-list)
.
你的例子
(+ 2 3 4
)只能因为您事先知道参数的数量而起作用。折叠在列表上的大小可能会有所不同。fold
/reduce
是“ cdr-down list”模式的一般版本。每个算法都涉及处理顺序的每个元素并从中计算一些返回值。它基本上是foreach循环的功能版本。
使用折叠的代码通常很尴尬。这就是为什么人们更喜欢地图,过滤器,存在,总和等等的原因。这些天,我主要是写编译器和口译员。这是我使用折叠的一些方法:
- 计算功能,表达或类型的自由变量集
- 将函数的参数添加到符号表中,例如,用于键入检查
- 累积从一系列定义生成的所有明智错误消息的集合
- 在启动时间将所有预定义的类添加到Smalltalk解释器
所有这些用途的共同点是,他们正在积累有关A的信息 序列 变成某种 放 或者 字典. 非常实用。
这是一个没有其他人提到的例子。
通过将功能与“折叠”(fold''这样的小定义的界面(例如“ fold”)),您可以替换该实现,而无需破坏使用程序的程序。例如,您可以做一个 在数千个PC上运行的分布式版本, ,因此使用它的分类算法将成为 分布式排序, , 等等。您的程序变成了 更健壮,更简单,更快.
您的例子是一个琐碎的例子: +
已经进行了许多参数,在很少的记忆中很快运行,并且已经由谁写成的编译器编写和调试。这些属性通常不需要我需要运行的算法。