首先,当你只有一个测试并且带有默认的t子句时,请使用if而不是cond。
此外,您正在使用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)))
考虑到您可能需要在下个任务中将列表分成两个以上,这里提供一种更通用的版本,仍然使用循环:
(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)
。
length
不是一个好主意。它必须每次遍历列表l
。 - Rainer Joswig