如何按cdr元素对cons列表进行排序?
按cons元素的cdr排序(或符号中的数字部分排序)
嘿,作为Lisp新手,这个问题其实很适合用标准库的sort函数来解决,不过先得澄清一个语法细节——你示例里的aaa.4写法容易混淆:在Common Lisp里,(aaa . 4)是真正的cons对(car是符号aaa,cdr是数字4),而aaa.4是一个单独的符号(名字包含点和数字)。我会两种情况都给你讲清楚:
情况1:元素是真正的cons对
如果你的输入是由(符号 . 数字)这样的cons对组成的列表,比如((aaa . 4) (bbb . 2) (ccc . 6) (ddd . 9) (eee . 3)),直接用sort加一个自定义的比较谓词就行:
;; 注意:sort是破坏性函数,会修改原列表,用copy-list保留原列表 (sort (copy-list '((aaa . 4) (bbb . 2) (ccc . 6) (ddd . 9) (eee . 3))) (lambda (x y) (< (cdr x) (cdr y))))
运行后会得到你想要的结果:((bbb . 2) (eee . 3) (aaa . 4) (ccc . 6) (ddd . 9))
代码解释:
copy-list:复制原列表,避免sort修改你原来的输入列表(Common Lisp的sort会直接修改传入的序列,这是它的特性)- 匿名函数
(lambda (x y) (< (cdr x) (cdr y))):作为排序的判断规则,每次取两个cons对x和y,比较它们的cdr部分,只要x的cdr比y小,就把x排在y前面,实现升序排序。
情况2:元素是带数字的符号
如果你的输入确实是(aaa.4 bbb.2 ...)这种符号组成的列表,那需要先从符号名里提取出后面的数字,再排序。我们可以写一个小函数来提取数字:
;; 从符号中提取点后面的数字 (defun extract-number-from-symbol (sym) (let* ((sym-str (string sym)) (dot-pos (position #\. sym-str))) (parse-integer (subseq sym-str (1+ dot-pos))))) ;; 排序代码 (sort (copy-list '(aaa.4 bbb.2 ccc.6 ddd.9 eee.3)) (lambda (x y) (< (extract-number-from-symbol x) (extract-number-from-symbol y))))
运行后会输出:(bbb.2 eee.3 aaa.4 ccc.6 ddd.9),完全符合你的示例要求。
代码解释:
extract-number-from-symbol:把符号转成字符串,找到点的位置,截取点后面的子字符串,再转成整数。比如aaa.4会被转换成字符串"AAA.4",提取出"4"并转为整数4。- 排序时,用这个函数提取两个符号的数字部分,比较大小后完成排序。
新手小提示
- 一定要注意cons对的语法:
(car . cdr)里的点前后必须有空格,否则会被当成一个符号,这是新手很容易踩的坑。 - 如果你不需要保留原列表,可以去掉
copy-list,直接给sort传原列表就行,但一般建议保留原数据,避免意外修改。
内容的提问来源于stack exchange,提问作者Леонид Захаров




