匿名函数 #
一、匿名函数简介 #
匿名函数是没有名字的函数,常用于一次性操作或作为高阶函数的参数。Clojure提供了多种创建匿名函数的方式。
1.1 为什么需要匿名函数 #
clojure
(map (fn [x] (* x x)) [1 2 3 4 5])
(map #(* % %) [1 2 3 4 5])
匿名函数让代码更简洁,避免为简单操作定义命名函数。
二、fn形式 #
2.1 基本语法 #
clojure
(fn [params] body)
(fn [x] (* x x))
((fn [x] (* x x)) 5)
2.2 多参数 #
clojure
(fn [a b] (+ a b))
((fn [a b] (+ a b)) 3 4)
2.3 可变参数 #
clojure
(fn [x & rest]
(cons x rest))
((fn [x & rest] (cons x rest)) 1 2 3 4 5)
2.4 多参数列表 #
clojure
(fn
([x] x)
([x y] (+ x y)))
(def add (fn
([x] x)
([x y] (+ x y))))
(add 5)
(add 3 4)
2.5 命名匿名函数 #
clojure
(fn factorial [n]
(if (<= n 1)
1
(* n (factorial (dec n)))))
((fn factorial [n]
(if (<= n 1)
1
(* n (factorial (dec n)))))
5)
三、#()语法 #
3.1 基本语法 #
clojure
#(* % %)
(#(* % %) 5)
(map #(* % %) [1 2 3 4 5])
3.2 参数占位符 #
clojure
#(+ % 1)
#(+ %1 %2)
#(+ %1 %2 %3)
#(* %1 %1)
| 占位符 | 含义 |
|---|---|
% |
第一个参数(等同于 %1) |
%1 |
第一个参数 |
%2 |
第二个参数 |
%n |
第n个参数 |
%& |
剩余参数 |
3.3 多参数示例 #
clojure
(#(+ %1 %2) 3 4)
(#(str %1 "-" %2) "a" "b")
(map #(vector %1 %2) [1 2 3] [:a :b :c])
3.4 可变参数 #
clojure
(#(apply + %&) 1 2 3 4 5)
(#(vector %1 %&) 1 2 3 4 5)
3.5 嵌套表达式 #
clojure
#(str "Hello, " % "!")
#(let [x (* % 2)]
(str "Double: " x))
#(if (even? %)
"even"
"odd")
四、闭包 #
4.1 什么是闭包 #
闭包是捕获外部作用域变量的函数:
clojure
(defn make-adder [n]
(fn [x] (+ x n)))
(def add5 (make-adder 5))
(def add10 (make-adder 10))
(add5 3)
(add10 3)
4.2 捕获多个变量 #
clojure
(defn make-multiplier [factor offset]
(fn [x]
(+ (* x factor) offset)))
(def calc (make-multiplier 2 10))
(calc 5)
4.3 状态封装 #
clojure
(defn make-counter []
(let [count (atom 0)]
(fn []
(swap! count inc))))
(def counter (make-counter))
(counter)
(counter)
(counter)
4.4 配置闭包 #
clojure
(defn make-logger [prefix]
(fn [message]
(println (str prefix ": " message))))
(def error-log (make-logger "ERROR"))
(def info-log (make-logger "INFO"))
(error-log "Something went wrong")
(info-log "Process completed")
五、词法作用域 #
5.1 作用域规则 #
Clojure使用词法作用域,函数捕获定义时的环境:
clojure
(def x 10)
(defn outer []
(let [x 20]
(fn [] x)))
((outer))
(def inner (outer))
(inner)
5.2 嵌套作用域 #
clojure
(defn make-nested [a]
(fn [b]
(fn [c]
(+ a b c))))
(def f1 (make-nested 1))
(def f2 (f1 2))
(f2 3)
5.3 let绑定 #
clojure
(let [x 10
f (fn [y] (+ x y))]
(f 5))
(let [x 10]
(let [f (fn [y] (+ x y))
x 20]
(f 5)))
六、匿名函数应用 #
6.1 作为高阶函数参数 #
clojure
(map #(inc %) [1 2 3])
(filter #(> % 3) [1 2 3 4 5])
(reduce #(+ %1 %2) 0 [1 2 3])
(sort-by #(:age %) [{:name "Bob" :age 25}
{:name "Alice" :age 30}])
6.2 条件处理 #
clojure
(filter #(and (even? %) (> % 2)) [1 2 3 4 5 6])
(remove #(or (nil? %) (empty? %)) [1 nil "" 2 "hello"])
6.3 数据转换 #
clojure
(map #(assoc % :processed true) [{:id 1} {:id 2}])
(map #(update % :count inc) [{:count 1} {:count 2}])
6.4 回调函数 #
clojure
(defn process-with-callback [data callback]
(callback (map inc data)))
(process-with-callback [1 2 3] #(reduce + %))
七、fn vs #() #
7.1 使用场景 #
使用#()当:
- 函数体简单
- 单行表达式
- 作为高阶函数参数
clojure
(map #(* % 2) [1 2 3])
(filter #(> % 5) [1 2 3 4 5 6 7])
使用fn当:
- 函数体复杂
- 需要多行
- 需要命名(递归)
- 需要多参数列表
clojure
(map (fn [x]
(let [y (* x x)]
(if (> y 10)
y
0)))
[1 2 3 4 5])
7.2 限制对比 #
| 特性 | fn |
#() |
|---|---|---|
| 多行表达式 | 支持 | 不支持 |
| 嵌套函数 | 支持 | 不支持 |
| 多参数列表 | 支持 | 不支持 |
| 命名递归 | 支持 | 不支持 |
| 简洁性 | 较长 | 简洁 |
八、实践示例 #
8.1 延迟计算 #
clojure
(defn lazy-computation [f]
(fn []
(f)))
(def expensive (lazy-computation
#(reduce + (range 1000000))))
(expensive)
8.2 函数工厂 #
clojure
(defn make-validator [pred message]
(fn [value]
(when-not (pred value)
message)))
(def positive? (make-validator pos? "Must be positive"))
(def non-empty? (make-validator (complement empty?) "Must not be empty"))
(positive? 5)
(positive? -1)
8.3 事件处理 #
clojure
(defn make-handler [action]
(fn [event]
(action (:data event))))
(def handle-save (make-handler #(println "Saving:" %)))
(def handle-delete (make-handler #(println "Deleting:" %)))
(handle-save {:data {:id 1 :name "Item"}})
8.4 柯里化 #
clojure
(defn curry [f]
(fn [& args]
(if (>= (count args) 2)
(apply f args)
(fn [x]
(apply f (conj args x))))))
(def curried-add (curry +))
((curried-add 1) 2)
(curried-add 1 2)
九、常见陷阱 #
9.1 #()中的嵌套函数 #
clojure
(#(fn [y] (+ % y)) 5)
(#((fn [y] (+ % y))) 5)
9.2 % vs %1 #
clojure
(#(vector % %1) 1)
(#(vector % %2) 1 2)
9.3 闭包中的变量 #
clojure
(def functions
(for [i (range 5)]
(fn [] i)))
(map (fn [f] (f)) functions)
十、总结 #
匿名函数语法:
| 语法 | 用途 |
|---|---|
(fn [x] body) |
完整形式 |
#(* % %) |
简洁形式 |
#(+ %1 %2) |
多参数 |
关键点:
fn是完整形式,#()是语法糖- 闭包捕获定义时的环境
- 词法作用域决定变量绑定
- 选择合适的形式提高可读性
下一步,让我们学习多态与多重方法!
最后更新:2026-03-27