diff options
Diffstat (limited to 'content')
| -rw-r--r-- | content/article/clj8.md | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/content/article/clj8.md b/content/article/clj8.md new file mode 100644 index 00000000..aa2442c1 --- /dev/null +++ b/content/article/clj8.md @@ -0,0 +1,100 @@ ++++ +title = 'Coffee Break Clojure, Fold' +subtitle = '' +slug = 'clj8' +tags = ["Clojure","Lisp","编程"] +categories = '茶歇蔻芥' +date = 2026-10-17T17:45:13+08:00 +draft = true +toc = true ++++ + +在第三期《[只用圆括号怎么够?](/article/clj3/#reduce)》里,我讲到了 `reduce` 函数,先来简单复习一下:`reduce` 最少接收两个参数,形如 `(reduce f coll)`,其中 `f` 是接收两个参数的函数,形如 `(f x y)`;`reduce` 遍历 `coll` 中的每个元素,现对前两个元素求 `(f coll1 coll2)`,然后对第三个参数求 `(f (f coll1 coll2) coll3)`,以此类推。<!--more--> + +`(f x y)` 这种函数叫作归约函数(reducing function)。 + +## 归约、Fold、高阶函数 + +之前我们用作示例的代码如下: + +```clojure +(def numbers '(1 43 231 23 99)) + +(reduce + numbers) +;; => 397 +``` + +`+` 可以视作归约函数,因为他可以这样调用:`(+ x y)`。当我们对上述代码进行求值时,主要执行了以下步骤: + +1. `(+ 1 43)` 得到 `44` +2. `(+ 44 231)` 得到 `275` +3. `(+ 275 23)` 得到 `298` +4. `(+ 298 99)` 得到 `397` + +使用 `+` 作为归约函数,对 `numbers` 这个数字列表进行归约(reduce),就达到了累加的效果。在函数式编程里,归约(reduce)也叫作累积(accumulate)、聚集(accumulate)、压缩(compress)和注入(inject),不过更常见的名字是折叠(fold)。 + +还记得第三篇讲过的 [Cons](/article/clj3/#圆括号和列表) 和 Lisp 的列表构造方式吗?Fold 的结构就像是函数版本的 Cons。 + +<div class="md:flex"> + +<div class="md:w-1/2"> + +```clojure +(reduce f [1 2 3 4 5]) +;; 等价于 +(f (f (f (f 1 2) 3) 4) 5) +``` + +</div> + +<div class="md:w-1/2"> + +```clojure +(list 1 2 3 4 5) +;; 等价于 +(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 nil))))) +``` + +</div> + +</div> + +Fold 有方向,Clojure 的 `reduce` 默认是左折叠,即 `foldl`: + +)") + +要实现右折叠很简单,只要把待操作的列表倒序就好: + +```clojure +(reduce f (reverse [1 2 3 4 5])) +;; 等价于 +(f (f (f (f 5 4) 3) 2) 1) +``` + +Fold 中的 `f` 本身也可以是 Fold。还记得 `into` 吗? + +```clojure +(into [1 2 3] [4 5]) +;; 等价于 +(reduce conj [1 2 3] [4 5]) ;; 这里的 reduce 以 [1 2 3] 为初始值 + ;; 如果没有初始值,就会用第一个元素做初始值 +;; 等价于 +(conj (conj (conj [1 2 3]) 4) 5) +``` + +如果要打平一个向量,比如把 `[[1] [2 3] [4 5]]` 变成 `[1 2 3 4 5]`,就可以用 `into` 作为归约函数(reducing function),也就是 Fold 中的 `f`。 + +```clojure +(reduce into [[1] [2 3] [4 5]]) +;; 等价于 +(reduce #(reduce conj %1 %2) [[1] [2 3] [4 5]]) +;; 等价于 +(reduce conj (reduce conj [1] [2 3]) [4 5]) +;; 等价于 +(conj (conj (conj (conj [1] 2) 3) 4) 5) +``` + +在 Clojure 中,能够被放在 `reduce` 里使用的函数就叫作归约函数(reducing function)。这个概念与函数式编程和 Lambda 演算的高阶函数(Higher-order function)有关,据说微积分中的导数也算一种高阶函数,但我这篇文章不是讲数学的(我也不会数学),所以就点到为止。 + + + |
