Во-первых, пустой список - это палиндром! Если мы перевернем его, мы получим тот же пустой список.
Во-вторых, функции Lisp не выводят свои значения результатов; они возвращают эти значения.
В интерактивном сеансе именно слушатель печатает результирующие значения, которые появляются из вычисляемого выражения. Само это выражение не должно ничего печатать.
Поэтому мы начинаем так:
(defun palindromep (l)
(cond
((null l) t) ;; the empty list is a palindrome: yield true.
Обратите внимание, кстати, что если мы напишем это:
((null l) nil t) ;; the empty list is a palindrome: yield true.
это ничего не делает. Выражается дополнительное выражение nil
, которое выдает nil
, которое выбрасывается. Компилятор Lisp полностью устранит это.
Что если список вообще не является списком, а атомом, отличным от nil
? Давайте просто 1049 с палиндромом. Однако необходимо уточнить требования:
((atom l) t)
Теперь мы знаем, что имеем дело с непустым списком. Если в нем ровно один предмет, то это палиндром:
((null (cdr l)) t)
Теперь мы знаем, что имеем дело со списком из двух или более предметов. Это палиндром, если первый и последний элементы совпадают, и если элементы между ними образуют палиндром.
(t (let* ((first (car l))
(rest (cdr l))
(tail (last l))
(interior (ldiff rest tail)))
(and (eql first (car tail)) (palindromep interior))))))
Все это:
(defun palindromep (l)
(cond
((null l) t)
((atom l) t)
((null (cdr l)) t)
(t (let* ((first (car l))
(rest (cdr l))
(tail (last l))
(interior (ldiff rest tail)))
(and (eql first (car tail)) (palindromep interior))))))
Код игры в гольф: в конструкции cond
, описанной ANSI CL, предложение может иметь только одну форму. Если эта форма дает истинное значение, то это значение возвращается. Таким образом, мы можем удалить t
:
(defun palindromep (l)
(cond
((null l)) ;; t removed
((atom l)) ;; likewise
((null (cdr l))) ;; likewise
(t (let* ((first (car l))
(rest (cdr l))
(tail (last l))
(interior (ldiff rest tail)))
(and (eql first (car tail)) (palindromep interior))))))
Документация о функциях ldiff
и last
может быть найдена здесь .
Дальнейшая игра в гольф : если у нас есть этот шаблон (cond (A) (B) ... (t Z))
, мы можем просто заменить его на (or A B ... Z)
:
(defun palindromep (l)
(or (null l)
(atom l)
(let* ((first (car l))
(rest (cdr l))
(tail (last l))
(interior (ldiff rest tail)))
(and (eql first (car tail)) (palindromep interior)))))
cond
подобен обобщению or
, которое может указывать альтернативное значение результата для каждого завершения истинный случай.