LISP - 序列
Lisp - 使用序列
Section titled “Lisp - 使用序列”序列 是 Common Lisp 中一个基本抽象数据类型。它代表了一个元素的有序集合。最常见的两种具体序列类型是列表和向量。大多数序列函数在这两者上都有效,使你的代码灵活且通用。
1. 创建和访问序列
Section titled “1. 创建和访问序列”你可以直接创建序列,也可以使用 make-sequence 创建。访问元素是通过 elt 函数完成的。
;; 列表通过 ' 或 (list ...) 创建(defvar my-list '(a b c d e))
;; 向量通过 #(...) 创建(defvar my-vector #(10 20 30 40 50))
;; 以编程方式创建序列(defvar my-generated-vector (make-sequence 'vector 5 :initial-element 0));=> #(0 0 0 0 0)
;; 访问列表的第 3 个元素(0 索引)(format t "List element at index 2: ~a~%" (elt my-list 2)) ;=> C
;; 访问向量的第 4 个元素(format t "Vector element at index 3: ~a~%" (elt my-vector 3)) ;=> 40
;; 获取序列的长度(format t "Length of my-list: ~d~%" (length my-list)) ;=> 52. 修改序列:破坏性 vs. 非破坏性
Section titled “2. 修改序列:破坏性 vs. 非破坏性”Lisp 中的一个关键概念是区分原地修改序列的函数(破坏性)和返回新修改序列的函数(非破坏性)。
- 非破坏性:更安全,但由于需要分配新内存,效率可能较低。示例:
remove、substitute、reverse。 - 破坏性:更节省内存,但会修改原始数据。通常以
n为前缀(例如nreverse),或以delete或fill等动词命名。谨慎使用!
示例:移除和替换元素
Section titled “示例:移除和替换元素”(let ((data '(1 5 2 5 3 5))) ;; REMOVE(非破坏性)返回一个新列表 (let ((new-data (remove 5 data))) (format t "Original data after REMOVE: ~a~%" data) ;=> (1 5 2 5 3 5) (format t "Result of REMOVE: ~a~%" new-data)) ;=> (1 2 3) (terpri)
;; DELETE(破坏性)修改原始列表结构 (let ((modifiable-data (list 1 5 2 5 3 5))) (delete 5 modifiable-data) (format t "Data after DELETE: ~a~%" modifiable-data)) ;=> (1 2 3) (or similar) (terpri)
;; SUBSTITUTE(非破坏性) (let ((substituted (substitute 99 5 data :count 1))) (format t "Result of SUBSTITUTE: ~a~%" substituted)) ;=> (1 99 2 5 3 5))3. 高阶函数:映射和过滤
Section titled “3. 高阶函数:映射和过滤”Lisp 擅长将函数作为数据使用。你可以将函数应用于序列以转换或过滤它们,而无需编写显式循环。
(defvar numbers '(1 2 3 4 5 6))
;; MAPCAR 将函数应用于列表的每个元素(let ((squared (mapcar #'(lambda (x) (* x x)) numbers))) (format t "Squared numbers: ~a~%" squared)) ;=> (1 4 9 16 25 36)
;; REMOVE-IF-NOT(过滤)保留满足谓词的元素(let ((even-numbers (remove-if-not #'evenp numbers))) (format t "Even numbers: ~a~%" even-numbers)) ;=> (2 4 6)
;; COUNT-IF 统计满足谓词的元素(let ((odd-count (count-if #'oddp numbers))) (format t "Count of odd numbers: ~d~%" odd-count)) ;=> 3
;; REDUCE 使用二元函数组合元素(let ((sum (reduce #'+ numbers))) (format t "Sum of numbers: ~d~%" sum)) ;=> 214. 排序和合并
Section titled “4. 排序和合并”sort 函数原地修改序列,而 merge 将两个已排序的序列合并。
(let ((unsorted-data (list 9 1 8 2 7 3))) ;; SORT 是破坏性的! (sort unsorted-data #'<) (format t "Sorted data: ~a~%" unsorted-data)) ;=> (1 2 3 7 8 9)
(let ((list1 '(1 5 10)) (list2 '(2 4 12))) ;; MERGE 是非破坏性的 (let ((merged (merge 'list list1 list2 #'<))) (format t "Merged list: ~a~%" merged))) ;=> (1 2 4 5 10 12)5. 序列谓词
Section titled “5. 序列谓词”这些函数测试序列的元素是否满足条件。它们效率很高,因为一旦结果已知(短路求值),它们就会停止。
(defvar nums '(2 4 6 8))(defvar mixed-nums '(1 2 3 4))
;; EVERY - 所有元素都是偶数吗?(format t "All even in ~a? ~a~%" nums (every #'evenp nums)) ;=> T(format t "All even in ~a? ~a~%" mixed-nums (every #'evenp mixed-nums)) ;=> NIL
;; SOME - 至少有一个元素是奇数吗?(format t "Some odd in ~a? ~a~%" nums (some #'oddp nums)) ;=> NIL(format t "Some odd in ~a? ~a~%" mixed-nums (some #'oddp mixed-nums)) ;=> T有用的关键字参数
Section titled “有用的关键字参数”许多序列函数接受关键字参数来定制其行为:
| Argument | Meaning | Default |
|---|---|---|
| :test | 一个双参数函数,用于比较元素(例如,用于嵌套列表的 #'equal)。 | #'eql |
| :key | 一个单参数函数,用于在比较前从元素中提取值。 | #'identity |
| :start, :end | 对从起始索引到(但不包括)结束索引的子序列进行操作。 | 0, nil |
| :from-end | 如果为真,则从右到左遍历序列。 | nil |
| :count | 限制受影响的元素数量(remove、substitute)。 | nil |