summaryrefslogtreecommitdiff
path: root/content/article
diff options
context:
space:
mode:
authoreltrac <hi@eltr.ac>2026-10-17 18:40:14 +0800
committereltrac <hi@eltr.ac>2026-10-17 18:40:14 +0800
commitdcade465c001d0ae2f2f486683ca75ebe3a5aa40 (patch)
treecccedb1a64d6a81eaf26aae5941b045f7cce9e87 /content/article
parent77025680498e956b4e29a6b3b5c1a01cd654c462 (diff)
[draft] new clj8
Diffstat (limited to 'content/article')
-rw-r--r--content/article/clj8.md100
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`:
+
+![](https://img.eltr.ac/geedeapro/article/clj8/Left-fold-transformation.png "左折叠示意图;图源:[维基百科](https://zh.wikipedia.org/wiki/Fold_(%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0))")
+
+要实现右折叠很简单,只要把待操作的列表倒序就好:
+
+```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)有关,据说微积分中的导数也算一种高阶函数,但我这篇文章不是讲数学的(我也不会数学),所以就点到为止。
+
+
+