Список представляет собой цепочку из cons
пар. например. (1 2 3)
- это визуализация (1 . (2 . (3 . ())))
. В случае, если последний cdr
не является ()
, у вас есть то, что мы называем пунктирным списком, поскольку упрощенная визуализация последней части отсутствует. Он должен быть напечатан с точкой.
У вас есть (E . (B . (C . (D . A))))
и вы хотите иметь (E . (B . (C . (D . (A . ())))))
. Вы видите разницу? (car list)
это не список, а один элемент, и поэтому вы получаете пунктирный список.
Вот более разумные реализации append
и butlast
:
(defun my-append (a b)
(if (null a)
b
(cons (car a) (my-append (cdr a) b))))
Это поддерживает только 2 аргумента, но идея для большего заключается в том, что он продолжается до тех пор, пока вы не осмотрите все предыдущие списки и не останетесь только у одного, который дословно становится хвостом. Вот как это может выглядеть:
(defun my-append2 (x &rest xs)
(labels ((helper (x xs)
(cond ((null xs) x)
((null x) (helper (car xs) (cdr xs)))
(t (cons (car x) (helper (cdr x) xs))))))
(helper x xs)))
Вот butlast
(defun my-butlast (xs)
(if (null (cdr xs))
'()
(cons (car xs) (my-butlast (cdr xs)))))
Теперь действительно нужно сделать это с функциями более высокого порядка или loop
, но тогда вы получите скрытые факты о том, как работают списки. Код выше показывает, что они работают.