Lisp实现列表偶数索引元素封装为单元素列表的函数问题
搞定Lisp列表偶数位置元素包裹的问题
嘿,作为Lisp新手能写出递归思路已经很棒啦!先帮你理清楚:从你的例子来看,你其实是想把**列表里第2、4、6…个元素(从1开始数)**包成单元素列表,对应0开始的索引就是奇数位置的元素~你的现有代码问题在于没区分元素的位置,所以每个都被包裹了,咱们来调整下。
先说说你原代码的小问题
你的代码(DEFUN ON3 (lst) ((ATOM (CDR lst)) (CONS (CONS (CAR lst) NIL) NIL)) (CONS (CONS (CAR lst) NIL) (ON3 (CDR lst))))有两个关键问题:
- 条件判断的语法错了:Lisp里判断条件得用
cond或者if,你直接写((ATOM (CDR lst)) ...)会被当成函数调用,根本起不到判断作用; - 没有位置区分:不管当前处理的是第几个元素,都直接把它包成单元素列表,自然所有元素都被包裹啦。
方案1:带索引的递归函数
咱们给函数加个可选的索引参数(默认从0开始),每次递归时索引加1,判断当前索引是不是奇数(对应你要的偶数位置元素):
(defun wrap-even-positions (lst &optional (index 0)) (cond ((null lst) nil) ; 空列表直接返回空 ((oddp index) ; 奇数索引(第2、4…个元素),包裹起来 (cons (list (car lst)) (wrap-even-positions (cdr lst) (+ index 1)))) (t ; 其他情况直接保留原元素 (cons (car lst) (wrap-even-positions (cdr lst) (+ index 1))))))
测试下效果:
(wrap-even-positions '(1 2 3 4 5)) ; 输出 (1 (2) 3 (4) 5) (wrap-even-positions '(1 2 3 4 5 6)) ; 输出 (1 (2) 3 (4) 5 (6))
方案2:封装辅助函数的递归(更地道的Lisp风格)
如果不想让主函数带额外参数,可以把索引处理封装在内部辅助函数里,对外接口更干净:
(defun wrap-even-positions (lst) (labels ((helper (remaining current-index) (cond ((null remaining) nil) ((oddp current-index) (cons (list (car remaining)) (helper (cdr remaining) (+ current-index 1)))) (t (cons (car remaining) (helper (cdr remaining) (+ current-index 1))))))) (helper lst 0)))
这个版本和方案1逻辑完全一样,只是把索引相关的逻辑藏在了helper函数里,调用的时候不用管索引参数,更友好。
额外小技巧:用loop宏快速实现
要是你已经接触过Lisp的loop宏,用它写会更简洁直观,一行核心逻辑搞定:
(defun wrap-even-positions (lst) (loop for elem in lst for i from 0 collect (if (oddp i) (list elem) elem)))
这个版本直接遍历列表,同时跟踪索引i,判断后决定是否包裹元素,新手熟悉后会觉得很好用。
内容的提问来源于stack exchange,提问作者Jacky
相关产品推荐
相关产品推荐

