LISP - 在列表上使用 `reduce`
Lisp - 使用 reduce 折叠列表
Section titled “Lisp - 使用 reduce 折叠列表”reduce 是 Common Lisp 中用于处理序列的强大高阶函数。它通过反复将给定函数应用于序列的元素,将序列(如列表或向量)“规约”为一个单个值。这种模式在其他编程语言中也称为“折叠”(fold)或“累加”(accumulate)。
(reduce function sequence &key from-end start end initial-value key)其中:
function:一个函数(或一个命名函数的符号),它接受两个参数:累加结果和下一个元素。sequence:要处理的列表、向量或其他序列。:initial-value(可选):规约的起始值。如果未提供,则使用序列的第一个(或前几个)元素作为起始。:from-end(可选):如果为真 (t),规约将从最右侧元素开始,而不是从左侧。:key(可选):一个函数,在每个元素传递给规约function之前对其应用。
示例 1:数字求和
Section titled “示例 1:数字求和”reduce 最经典的用法是求数字列表的和。
main.lisp
Section titled “main.lisp”(print (reduce #'+ '(1 2 3 4)))
;; 工作原理:; 1. (+ 1 2) -> 3; 2. (+ 3 3) -> 6; 3. (+ 6 4) -> 10Output
Section titled “Output”10示例 2:查找最大数字
Section titled “示例 2:查找最大数字”你可以使用任何接受两个参数的函数,例如 max。
main.lisp
Section titled “main.lisp”(print (reduce #'max '(1 12 3 41 25)))
;; 工作原理:; 1. (max 1 12) -> 12; 2. (max 12 3) -> 12; 3. (max 12 41) -> 41; 4. (max 41 25) -> 41Output
Section titled “Output”41使用 :initial-value
Section titled “使用 :initial-value”initial-value 为累加提供了起始点。这在规约空列表时至关重要,否则会引发错误。
;; 求和从 100 开始,而不是从第一个元素开始。(print (reduce #'+ '(1 2 3 4) :initial-value 100))
;; 如果列表为空,则返回初始值。(print (reduce #'+ '() :initial-value 0))Output
Section titled “Output”1100使用 :from-end 从右侧规约
Section titled “使用 :from-end 从右侧规约”对于非结合(non-associative)函数(如减法),顺序很重要。:from-end 会反转操作顺序。
;; 从左到右(默认):((10 - 5) - 2) -> 3(print (reduce #'- '(10 5 2)))
;; 从右到左:(10 - (5 - 2)) -> 7(print (reduce #'- '(10 5 2) :from-end t))Output
Section titled “Output”37使用 lambda 实现自定义逻辑
Section titled “使用 lambda 实现自定义逻辑”你可以提供一个自定义的匿名函数 (lambda) 来进行更复杂的规约,例如查找列表中最长字符串的长度。
(let ((words '("lisp" "is" "powerful"))) (print (reduce (lambda (longest-len current-word) (max longest-len (length current-word))) words :initial-value 0)))Output
Section titled “Output”8常见陷阱和最佳实践
Section titled “常见陷阱和最佳实践”- 在空列表上使用
reduce:在空序列上调用reduce且不提供:initial-value将会报错。如果序列可能为空,请务必提供:initial-value。 reduce与loop:reduce非常适合简洁、函数式风格的单值累加。对于涉及副作用、多个累加器或复杂终止条件的更复杂迭代,loop宏通常更具可读性和强大性。