LISP使用append时保留元素括号及实现grouper函数的问题咨询
解决LISP中使用append时保留元素括号的问题
首先,咱们来拆解下你代码里括号消失的原因:
你当前的代码中,初始化的l是(list (car l1) (car l2)),得到的是(1 4)——这是一个扁平列表,而非包含子列表的嵌套结构。之后每次循环里的(append l (list (car l1) (car l2))),是把(2 5)、(3 6)这类扁平列表直接拼接到l后面,append的作用就是合并两个列表的元素,自然就会得到(1 4 2 5 3 6)这种完全扁平的结果,而非你想要的嵌套结构。
要实现((1 4)(2 5)(3 6))的效果,核心是把每一对元素先包裹成子列表,再将这些子列表收集起来组成最终的嵌套列表。下面给你两种解决方案:
方案一:使用Loop的Collect子句(推荐)
这是LISP中处理这类列表收集任务最惯用的写法,代码简洁且可读性高:
(defun grouper (l1 l2) (loop while (and l1 l2) ; 只要两个列表都还有元素就继续循环 collect (list (car l1) (car l2)) ; 把当前一对元素包装成子列表,收集到结果中 do (setq l1 (cdr l1) l2 (cdr l2)))) ; 移动两个列表的指针
测试示例:
(grouper '(1 2 3) '(4 5 6)) ; 返回 ((1 4) (2 5) (3 6))
方案二:修改你原有的代码
如果想基于你原来的代码调整,需要改变初始化和append的逻辑,确保每次添加的是子列表而非子列表的元素:
(defun grouper1 (l1 l2) ;; 初始化时,先把第一对元素包装成子列表,再放到外层列表里 (setq l (list (list (car l1) (car l2)))) (loop (setq l1 (cdr l1)) ;; 直接判断l1是否为空,比判断(car l1)更稳妥(避免列表包含nil元素的情况) (when (null l1) (return l)) (setq l2 (cdr l2)) ;; 把新的子列表放到一个列表中,再用append添加到结果里 (setq l (append l (list (list (car l1) (car l2))))) ) )
这个版本也能得到预期结果,但要注意:手动使用append的效率较低,因为每次append都需要遍历整个现有列表,当列表较长时性能会受影响,所以更推荐第一种方案。
内容的提问来源于stack exchange,提问作者Ziyin Zeng




