首先,当您的cond 只有一个测试和默认的t 子句时,请改用if。
另外,您使用的是first,但cadr; second 在您的上下文中比 cadr 更具可读性。
现在,将顺序交换为偶数列表。尝试逐步执行。手动操作可能有点乏味,但这有助于理解发生了什么。我个人更喜欢使用trace 宏:(trace split-list)。然后,运行您的示例:
0: (split-list (a b c 1 2 3))
1: (split-list (b c 1 2 3))
2: (split-list (c 1 2 3))
3: (split-list (1 2 3))
4: (split-list (2 3))
5: (split-list (3))
6: (split-list nil)
6: split-list returned (nil nil)
5: split-list returned ((3) nil)
4: split-list returned ((3) (2))
3: split-list returned ((1 3) (2))
2: split-list returned ((1 3) (c 2))
1: split-list returned ((b 1 3) (c 2))
0: split-list returned ((b 1 3) (a c 2))
不清楚?尝试使用一个奇数大小的列表:
0: (split-list (a b c 1 2))
1: (split-list (b c 1 2))
2: (split-list (c 1 2))
3: (split-list (1 2))
4: (split-list (2))
5: (split-list nil)
5: split-list returned (nil nil)
4: split-list returned ((2) nil)
3: split-list returned ((2) (1))
2: split-list returned ((c 2) (1))
1: split-list returned ((c 2) (b 1))
0: split-list returned ((a c 2) (b 1))
您似乎总是将最里面的结果存储在左侧列表中!
可能的递归实现大致如下:
(defun split-list (list)
(if (endp list)
'(nil nil)
(destructuring-bind (left right) (split-list (cddr list))
(list (cons (first list) left)
(if (second list)
(cons (second list) right)
right)))))
但是对于足够大的输入,这可能会使堆栈崩溃。供您参考,这里是loop 的简单非递归方法:
(defun split-list (list)
(loop for (a b) on list by #'cddr
collect a into left
when b
collect b into right
finally (return (list left right)))
而且由于您可能必须在下一次作业中将列表拆分为 2 个以上的列表,因此使用更通用的版本,仍然带有循环:
(defun split-list (list &optional (n 2))
(loop with a = (make-array n :initial-element nil)
for e in list
for c = 0 then (mod (1+ c) n)
do (push e (aref a c))
finally (return (map 'list #'nreverse a))))
(split-list '(a b c d e f g) 3)
=> ((a d g) (b e) (c f))
如果你想享受循环列表的乐趣,你也可以试试这个,它适用于任何序列,而不仅仅是列表:
(defun split-n (sequence &optional (n 2))
(let* ((ring (make-list n :initial-element nil))
(head ring)
(last (last ring)))
(setf (cdr last) ring)
(map nil
(lambda (u)
(push u (first ring))
(pop ring))
sequence)
(setf (cdr last) nil)
(map-into head #'nreverse head)))
如果您打算调查其工作原理,请先评估 (setf *print-circle* t)。