Functional programming 函数式语言中fold/reduce的实际应用

Functional programming 函数式语言中fold/reduce的实际应用,functional-programming,lisp,reduce,fold,Functional Programming,Lisp,Reduce,Fold,Fold(又称reduce)被认为是一个非常重要的高阶函数Map可以用fold()表示。但对我来说,这听起来更学术而不是实际。一个典型的用法可能是获取数字的和、积或最大值,但这些函数通常接受任意数量的参数。既然(+235)工作正常,为什么还要写(折叠+0'(235))。我的问题是,在什么情况下使用fold最简单或最自然?这里有几个简单的例子,其中reduce非常有效 查找每个子列表的最大值之和 Clojure: user=> (def x '((1 2 3) (4 5) (0 9 1)))

Fold
(又称
reduce
)被认为是一个非常重要的高阶函数
Map
可以用
fold
()表示。但对我来说,这听起来更学术而不是实际。一个典型的用法可能是获取数字的和、积或最大值,但这些函数通常接受任意数量的参数。既然
(+235)
工作正常,为什么还要写
(折叠+0'(235))
。我的问题是,在什么情况下使用
fold
最简单或最自然?

这里有几个简单的例子,其中
reduce
非常有效

查找每个子列表的最大值之和

Clojure:

user=> (def x '((1 2 3) (4 5) (0 9 1)))
#'user/x
user=> (reduce #(+ %1 (apply max %2)) 0 x)
17
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"
球拍:

> (define x '((1 2 3) (4 5) (0 9 1)))
> (foldl (lambda (a b) (+ b (apply max a))) 0 x)
17
从列表构建地图

Clojure:

user=> (def x '((1 2 3) (4 5) (0 9 1)))
#'user/x
user=> (reduce #(+ %1 (apply max %2)) 0 x)
17
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"
有关以
reduce
为特征的更复杂的clojure示例,请查看Project Euler problems&


另请参见:

折叠的要点在于它更抽象。这并不是说你可以做以前做不到的事情,而是你可以更容易地去做

使用
折叠
,可以将在两个元素上定义的任何函数概括为应用于任意数量的元素。这是一个胜利,因为编写、测试、维护和修改一个应用两个参数的函数通常比编写、测试、维护和修改一个列表容易得多。编写、测试、维护等都很容易。一个简单的函数,而不是两个功能相似但不完全相同的函数

由于
fold
(就此而言,
map
filter
和friends)具有定义良好的行为,因此使用这些函数通常比显式递归更容易理解代码

基本上,一旦你有了一个版本,你就可以“免费”得到另一个版本。最终,为了得到相同的结果,你会做更少的工作

  • 您的示例
    (+2 3 4
    )之所以有效,是因为您事先知道参数的数量。折叠用于列表,列表的大小可能会有所不同

  • fold
    /
    reduce
    是“cdr-ing-down-a-list”模式的通用版本。每个算法都是关于按顺序处理序列中的每个元素,并从中计算一些返回值,这些返回值可以用它来表示。它基本上是foreach循环的功能版本


  • 这是一个还没有人提到的例子

    通过使用具有小型、定义良好的接口(如“fold”)的函数,您可以在不破坏使用它的程序的情况下替换该实现。例如,你可以做一个,这样一个使用它的排序算法就会变成,等等。您的程序将成为


    您的示例很简单:
    +
    已经接受了任意数量的参数,在很少的内存中快速运行,并且已经由编写编译器的人编写和调试。对于我需要运行的算法,这些属性通常不正确。

    使用fold的代码通常难以阅读。这就是为什么人们更喜欢映射、过滤、存在、求和等等。这些天我主要写编译和翻译;以下是我使用折叠的一些方法:

    • 计算函数、表达式或类型的自由变量集
    • 将函数的参数添加到符号表中,例如,用于类型检查
    • 累积从一系列定义生成的所有可感知错误消息的集合
    • 在启动时将所有预定义类添加到Smalltalk解释器

    所有这些用途的共同点是,它们将序列的信息积累到某种集合或字典中非常实用。

    在常见的Lisp函数中,不接受任何数量的参数

    在每个常见的Lisp实现中都定义了一个常量,该常量必须大于等于50

    这意味着任何此类可移植编写的函数都应至少接受50个参数。但可能只有50人

    存在此限制是为了允许编译器可能使用优化的调用方案,而不提供可以传递无限数量参数的一般情况


    因此,要在可移植的公共Lisp代码中真正处理大型(大于50个元素)列表或向量,必须使用迭代构造、reduce、map和类似的方法。因此,也有必要不使用
    (应用'+large list)
    ,而是使用
    (reduce'+large list)

    如果
    +
    尚未接受超过2个参数,您将如何实现这样的版本?
    fold
    是许多高级有用函数(如
    map
    的构建块。与原始的
    折叠相比,您的程序更可能使用这些函数,但事实上,您可以实现几乎任何使用列表作为某种折叠的功能,这一点非常重要。