列表 #

一、列表简介 #

列表是Clojure(以及所有Lisp方言)最基础的数据结构。Clojure的列表是单向链表,适合在头部添加元素。

1.1 列表特点 #

  • 单向链表结构
  • 头部插入O(1)
  • 随机访问O(n)
  • 不可变、持久化
  • 代码即数据

1.2 创建列表 #

clojure
'(1 2 3 4 5)

(list 1 2 3)

(list)

(list 1 2 3 4 5)

'()
clojure
(class '(1 2 3))

二、基本操作 #

2.1 访问元素 #

clojure
(def my-list '(1 2 3 4 5))

(first my-list)

(second my-list)

(rest my-list)

(next my-list)

(nth my-list 2)

(nth my-list 10)

(nth my-list 10 :not-found)

(last my-list)

2.2 first vs peek #

clojure
(first '(1 2 3))

(peek '(1 2 3))

(first '())

(peek '())

2.3 rest vs next #

clojure
(rest '(1 2 3))

(next '(1 2 3))

(rest '(1))

(next '(1))

(rest '())

(next '())

rest 返回空序列而不是nil,next 返回nil。

2.4 添加元素 #

clojure
(conj '(2 3 4) 1)

(cons 1 '(2 3 4))

(into '(3 4 5) '(1 2))

conj 对列表在头部添加元素。

三、列表函数 #

3.1 判断函数 #

clojure
(list? '(1 2 3))

(list? [1 2 3])

(list? '())

(empty? '())

(empty? '(1))

(seq? '(1 2 3))

(seq? [1 2 3])

3.2 列表构建 #

clojure
(list 1 2 3)

(list* 1 2 '(3 4 5))

(repeat 5 1)

(repeat 3 "hello")

(range 5)

(range 1 10)

(range 0 10 2)

(iterate inc 0)

(take 5 (iterate inc 0))

3.3 列表长度 #

clojure
(count '(1 2 3 4 5))

(count '())

四、列表遍历 #

4.1 map #

clojure
(map inc '(1 2 3 4 5))

(map #(* % %) '(1 2 3 4 5))

(map str '(1 2 3) '("a" "b" "c"))

4.2 filter #

clojure
(filter even? '(1 2 3 4 5 6))

(filter #(< % 4) '(1 2 3 4 5))

(remove odd? '(1 2 3 4 5))

4.3 reduce #

clojure
(reduce + '(1 2 3 4 5))

(reduce + 0 '(1 2 3 4 5))

(reduce conj '() '(1 2 3))

4.4 for #

clojure
(for [x '(1 2 3 4 5)]
  (* x x))

(for [x '(1 2 3 4 5)
      :when (even? x)]
  x)

(for [x '(1 2 3)
      y '(a b c)]
  [x y])

五、列表与代码 #

5.1 代码即数据 #

Clojure代码本身就是列表:

clojure
(def code '(+ 1 2 3))

(eval code)

(first code)

(rest code)

(class code)

5.2 宏与列表 #

宏操作代码列表:

clojure
(defmacro when-let
  [binding & body]
  `(let [~binding]
     (when ~'result
       ~@body)))

5.3 quote与syntax-quote #

clojure
'(+ 1 2)

`(+ 1 2)

(let [x 5]
  `(+ ~x 1))

(let [x 5]
  '(+ x 1))

六、列表与其他序列 #

6.1 转换为列表 #

clojure
(seq [1 2 3])

(seq {:a 1 :b 2})

(seq #{1 2 3})

(apply list [1 2 3])

(into '() [1 2 3])

6.2 列表vs向量 #

操作 列表 向量
头部添加 O(1) O(n)
尾部添加 O(n) O(1)
随机访问 O(n) O(1)
创建语法 '(1 2 3) [1 2 3]

选择建议

  • 需要头部添加:使用列表
  • 需要随机访问:使用向量
  • 代码结构:使用列表

七、高级操作 #

7.1 split-at #

clojure
(split-at 3 '(1 2 3 4 5 6 7))

7.2 split-with #

clojure
(split-with #(< % 4) '(1 2 3 4 5 6))

7.3 partition #

clojure
(partition 2 '(1 2 3 4 5 6))

(partition 3 2 '(1 2 3 4 5 6 7 8))

(partition-all 3 '(1 2 3 4 5))

7.4 interleave #

clojure
(interleave '(1 2 3) '(a b c))

7.5 interpose #

clojure
(interpose 0 '(1 2 3 4 5))

八、常用模式 #

8.1 递归处理 #

clojure
(defn sum-list [lst]
  (if (empty? lst)
    0
    (+ (first lst)
       (sum-list (rest lst)))))

(sum-list '(1 2 3 4 5))

8.2 尾递归优化 #

clojure
(defn sum-list-tail [lst]
  (loop [lst lst
         acc 0]
    (if (empty? lst)
      acc
      (recur (rest lst)
             (+ acc (first lst))))))

(sum-list-tail '(1 2 3 4 5))

8.3 构建列表 #

clojure
(defn build-list [n]
  (loop [i n
         result '()]
    (if (zero? i)
      result
      (recur (dec i)
             (conj result i)))))

(build-list 5)

8.4 反转列表 #

clojure
(defn my-reverse [lst]
  (reduce conj '() lst))

(my-reverse '(1 2 3 4 5))

九、实践示例 #

9.1 链表节点 #

clojure
(defn make-node [value next]
  {:value value :next next})

(defn node-value [node]
  (:value node))

(defn node-next [node]
  (:next node))

(def my-linked-list
  (make-node 1
    (make-node 2
      (make-node 3 nil))))

9.2 队列实现 #

clojure
(defn make-queue []
  '())

(defn enqueue [queue item]
  (concat queue (list item)))

(defn dequeue [queue]
  {:item (first queue)
   :queue (rest queue)})

(-> '()
    (enqueue 1)
    (enqueue 2)
    (enqueue 3))

9.3 栈实现 #

clojure
(defn make-stack []
  '())

(defn push [stack item]
  (conj stack item))

(defn pop-stack [stack]
  {:item (first stack)
   :stack (rest stack)})

(defn peek-stack [stack]
  (first stack))

(-> '()
    (push 1)
    (push 2)
    (push 3))

十、总结 #

列表操作速查:

操作 函数
创建 '(), list, list*
头部 first, peek
尾部 rest, next, pop
添加 conj, cons
访问 nth, last
判断 list?, empty?, seq?

关键点:

  1. 列表是Lisp的核心数据结构
  2. 代码即数据,列表可以表示代码
  3. 头部操作高效,随机访问慢
  4. rest vs next 的区别
  5. 使用 conj 在头部添加元素

下一步,让我们学习向量!

最后更新:2026-03-27