循环与递归 #
一、循环与递归简介 #
Clojure作为函数式语言,没有传统意义上的循环语句(如for、while)。取而代之的是递归和序列操作。
1.1 函数式迭代 #
clojure
(map inc [1 2 3 4 5])
(filter even? (range 10))
(reduce + [1 2 3 4 5])
二、loop/recur #
2.1 基本语法 #
clojure
(loop [bindings]
body
(recur new-values))
(loop [i 0]
(when (< i 5)
(println i)
(recur (inc i))))
2.2 累加器模式 #
clojure
(defn sum-to [n]
(loop [i 1
sum 0]
(if (> i n)
sum
(recur (inc i) (+ sum i)))))
(sum-to 10)
2.3 尾递归优化 #
recur 实现尾递归优化,不会栈溢出:
clojure
(defn factorial [n]
(loop [n n
acc 1]
(if (<= n 1)
acc
(recur (dec n) (* acc n)))))
(factorial 10)
(factorial 1000)
2.4 多变量循环 #
clojure
(defn fibonacci [n]
(loop [a 0
b 1
count 0]
(if (= count n)
a
(recur b (+ a b) (inc count)))))
(map fibonacci (range 10))
2.5 循环终止条件 #
clojure
(defn find-first [pred coll]
(loop [coll coll]
(when (seq coll)
(if (pred (first coll))
(first coll)
(recur (rest coll))))))
(find-first even? [1 3 5 4 7 8])
(find-first pos? [-1 -2 3])
三、函数内递归 #
3.1 普通递归 #
clojure
(defn sum [n]
(if (<= n 0)
0
(+ n (sum (dec n)))))
(sum 10)
(sum 10000)
3.2 使用recur #
clojure
(defn sum-tail [n]
(letfn [(helper [n acc]
(if (<= n 0)
acc
(recur (dec n) (+ acc n))))]
(helper n 0)))
(sum-tail 10000)
3.3 内部辅助函数 #
clojure
(defn reverse-list [lst]
(loop [lst lst
acc '()]
(if (empty? lst)
acc
(recur (rest lst) (conj acc (first lst))))))
(reverse-list [1 2 3 4 5])
四、doseq #
4.1 基本语法 #
clojure
(doseq [x coll]
body)
(doseq [i [1 2 3 4 5]]
(println i))
4.2 多重循环 #
clojure
(doseq [x [1 2 3]
y [:a :b]]
(println [x y]))
4.3 条件过滤 #
clojure
(doseq [x (range 10)
:when (even? x)]
(println x))
(doseq [x (range 10)
:let [y (* x x)]
:when (> y 10)]
(println x "->" y))
4.4 解构绑定 #
clojure
(doseq [[k v] {:a 1 :b 2 :c 3}]
(println k "=" v))
(doseq [[i x] (map-indexed vector [:a :b :c])]
(println i ":" x))
4.5 返回值 #
doseq 总是返回 nil:
clojure
(doseq [x [1 2 3]]
(* x 2))
五、dotimes #
5.1 基本语法 #
clojure
(dotimes [i n]
body)
(dotimes [i 5]
(println i))
5.2 实际应用 #
clojure
(dotimes [i 3]
(println "Iteration:" i))
(dotimes [_ 10]
(print "."))
5.3 返回值 #
dotimes 返回 nil。
六、while #
6.1 基本语法 #
clojure
(while condition
body)
(def counter (atom 5))
(while (pos? @counter)
(println @counter)
(swap! counter dec))
6.2 使用场景 #
while 较少使用,通常用 loop/recur 替代:
clojure
(loop [counter 5]
(when (pos? counter)
(println counter)
(recur (dec counter))))
七、惰性序列 #
7.1 惰性求值 #
clojure
(defn naturals []
(iterate inc 0))
(take 5 (naturals))
(take 10 (naturals))
7.2 无限序列 #
clojure
(take 5 (repeat :x))
(take 5 (cycle [1 2 3]))
(take 10 (iterate #(* % 2) 1))
7.3 自定义惰性序列 #
clojure
(defn fib-seq []
(map first
(iterate (fn [[a b]] [b (+ a b)])
[0 1])))
(take 10 (fib-seq))
(defn powers-of [n]
(iterate #(* % n) 1))
(take 10 (powers-of 2))
7.4 惰性序列注意事项 #
clojure
(defn print-and-return [x]
(println "Computing:" x)
x)
(def lazy-result
(map print-and-return (range 5)))
(take 2 lazy-result)
八、for #
8.1 基本语法 #
for 是列表推导式,返回惰性序列:
clojure
(for [x [1 2 3 4 5]]
(* x x))
(for [x [1 2 3]
y [:a :b]]
[x y])
8.2 条件过滤 #
clojure
(for [x (range 10)
:when (even? x)]
x)
(for [x (range 10)
:let [y (* x x)]
:when (> y 10)]
[x y])
8.3 :while #
clojure
(for [x (range 10)
:while (< x 5)]
x)
8.4 实际应用 #
clojure
(defn pairs [coll]
(for [x coll
y coll
:when (< x y)]
[x y]))
(pairs [1 2 3 4])
(defn cartesian-product [coll1 coll2]
(for [x coll1
y coll2]
[x y]))
(cartesian-product [1 2] [:a :b])
九、实践示例 #
9.1 查找算法 #
clojure
(defn binary-search [coll target]
(loop [low 0
high (dec (count coll))]
(if (> low high)
nil
(let [mid (quot (+ low high) 2)
mid-val (nth coll mid)]
(cond
(= mid-val target) mid
(< mid-val target) (recur (inc mid) high)
:else (recur low (dec mid)))))))
(binary-search [1 2 3 4 5 6 7 8 9] 5)
(binary-search [1 2 3 4 5 6 7 8 9] 10)
9.2 排序算法 #
clojure
(defn quick-sort [coll]
(if (empty? coll)
coll
(let [pivot (first coll)
rest-coll (rest coll)]
(concat
(quick-sort (filter #(< % pivot) rest-coll))
[pivot]
(quick-sort (filter #(>= % pivot) rest-coll))))))
(quick-sort [3 1 4 1 5 9 2 6 5])
9.3 树遍历 #
clojure
(defn tree-seq-custom [branch? children root]
(lazy-seq
(if (branch? root)
(cons root (mapcat #(tree-seq-custom branch? children %)
(children root)))
[root])))
(def tree {:value 1
:children [{:value 2
:children [{:value 4} {:value 5}]}
{:value 3
:children [{:value 6}]}]})
(tree-seq-custom :children :children tree)
9.4 批处理 #
clojure
(defn process-batch [items batch-size f]
(loop [items items
results []]
(if (empty? items)
results
(let [batch (take batch-size items)
result (f batch)]
(recur (drop batch-size items)
(conj results result))))))
(process-batch (range 10) 3
(fn [batch] (reduce + batch)))
十、性能考虑 #
10.1 尾递归vs普通递归 #
clojure
(defn sum-recursive [n]
(if (<= n 0)
0
(+ n (sum-recursive (dec n)))))
(defn sum-tail [n]
(loop [n n acc 0]
(if (<= n 0)
acc
(recur (dec n) (+ acc n)))))
(time (sum-tail 100000))
(time (sum-recursive 10000))
10.2 惰性序列内存 #
clojure
(defn sum-range [n]
(reduce + (range n)))
(defn sum-loop [n]
(loop [i 0 acc 0]
(if (>= i n)
acc
(recur (inc i) (+ acc i)))))
十一、总结 #
循环与递归速查:
| 形式 | 用途 | 返回值 |
|---|---|---|
loop/recur |
尾递归循环 | 自定义 |
doseq |
副作用遍历 | nil |
dotimes |
计数循环 | nil |
for |
列表推导 | 惰性序列 |
while |
条件循环 | nil |
关键点:
- 使用
recur实现尾递归优化 doseq用于副作用,for用于生成序列- 惰性序列支持无限数据
- 优先使用序列函数(map/filter/reduce)
loop/recur是通用迭代工具
控制流系列完成!接下来学习宏!
最后更新:2026-03-27