Recursion Lisp中带递归的回文检查
我已经开发了代码来检查输入是否是回文,但我很难弄清楚如何打印输出。如果输入是回文,我希望输出返回“t”,如果不是,则返回“nil”。我想给自己的另一个挑战是不使用reverse函数,这就是为什么我的代码不是那么简单。提前谢谢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) )))))
(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)中运行。你的速度再快不过了。你的代码中没有任何地方有任何比较:两个列表元素是否相同。因此,它不能是回文检查器。当然,但我们避免了反转,因为这是必需的。其次,在他列出的测试中,他可能希望在子列表中进行递归测试。。。