Recursion Lisp中带递归的回文检查

Recursion Lisp中带递归的回文检查,recursion,lisp,common-lisp,palindrome,Recursion,Lisp,Common Lisp,Palindrome,我已经开发了代码来检查输入是否是回文,但我很难弄清楚如何打印输出。如果输入是回文,我希望输出返回“t”,如果不是,则返回“nil”。我想给自己的另一个挑战是不使用reverse函数,这就是为什么我的代码不是那么简单。提前谢谢 (defun palindromep(l) (cond ((null l) nil (write nil)) (t (append (list (car l)) (palindromep (cdr l)) (list (car l) )))))

我已经开发了代码来检查输入是否是回文,但我很难弄清楚如何打印输出。如果输入是回文,我希望输出返回“t”,如果不是,则返回“nil”。我想给自己的另一个挑战是不使用reverse函数,这就是为什么我的代码不是那么简单。提前谢谢

(defun palindromep(l)
  (cond ((null l) nil (write nil))
        (t (append (list (car l)) (palindromep (cdr l)) (list (car l) )))))


        (palindromep '(a b b a))
        (terpri)
        (palindromep '(a b c b a))
        (terpri)
        (palindromep '(a b c))
        (terpri)
        (palindromep '(a (d e) (d e) a))
        (terpri)
        (palindromep '(a (d e) (e d) a))

首先,空列表是回文!如果我们反转它,我们会得到相同的空列表

其次,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
的文档可以是

进一步的高尔夫运动:如果我们有这种模式
(cond(A)(B)…(tz))
,我们可以用
(或A B…Z)


cond
类似于
的泛化,可以为每个终止的真情况指定一个可选的结果值。

要继续玩代码高尔夫,因为需要
t
nil
,所以只能使用
nil
来表示条件(并使用
nil
表达式的短路)

此外,能够确定一个
:test
关键字也很好,因为您希望控制关键的测试行为。 为了能够同时使用内部列表,可以使用
equalp
甚至自定义比较函数

(defun palindromep (l &key (test #'equalp))
  (or (null l) (and (funcall test (car l) (car (last l)))
                    (palindromep (butlast (cdr l)) :test test))))
这评估了

(palindromep '(a (d e) (d e) a))
作为
t
但是

(palindromep '(a (d e) (e d) a))
nil
。 嗯,这可能是一个哲学问题,后者是否应该是
t
,前者是否应该是
nil

为了恢复这种行为,我们可以编写一个自定义测试函数

(defun palindromep (l &key (test #'equalp))
  (or (null l) (and (funcall test (car l) (car (last l)))
                    (palindromep (butlast (cdr l)) :test test))))
像这样:

(defun reverse* (l &optional (acc '()))
  (cond ((null l) acc)
        ((atom (car l)) (reverse* (cdr l) (cons (car l) acc)))
        (t (reverse* (cdr l) (cons (reverse* (car l) '()) acc)))))

(defun to-each-other-symmetric-p (a b)
  (cond ((and (atom a) (atom b)) (equalp a b))
        (t (equalp a (reverse* b)))))
嗯,我在这里使用了某种
反向*

如果有人这样做:

(palindromep '(a (d e) (d e) a) :test #'to-each-other-symmetric-p) ;; NIL
;; and
(palindromep '(a (d e) (e d) a) :test #'to-each-other-symmetric-p) ;; T

为了完成其他答案,我想指出,不使用
反向
不仅会使代码变得非常复杂,而且会使代码效率大大降低。只需将上述答案与经典答案进行比较:

(defun palindromep (l)
  (equal l (reverse l)))

reverse
是o(l),即它所需的时间与列表l的长度成比例,
也等于
。因此此函数将在o(l)中运行。你的速度再快不过了。

你的代码中没有任何地方有任何比较:两个列表元素是否相同。因此,它不能是回文检查器。当然,但我们避免了
反转,因为这是必需的。其次,在他列出的测试中,他可能希望在子列表中进行递归测试。。。