首先,空列表是回文!如果我们反转它,我们会得到相同的空列表。
其次,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 以外的原子怎么办?让我们以回文为例。但是,需要澄清要求:
((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))))))
代码打高尔夫球:在 ANSI CL 描述的cond 构造中,子句允许只有一种形式。如果该形式产生一个真值,则返回该值。因此我们可以删除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函数的文档可以是found here。
进一步打高尔夫球:如果我们有这种模式(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 的泛化,可以为每个终止的真情况指定一个替代结果值。