【问题标题】:Palindrome check with recursion in Lisp在 Lisp 中使用递归进行回文检查
【发布时间】:2020-02-20 18:15:51
【问题描述】:

我已经开发了代码来检查输入以查看它是否是回文,但我很难弄清楚如何打印输出。如果输入是回文,我希望输出返回“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))

【问题讨论】:

  • 您的代码中没有任何比较:两个列表元素是否相同。因此,它不能是回文检查器。

标签: recursion lisp common-lisp palindrome


【解决方案1】:

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

其次,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))))))

关于ldifflast函数的文档可以是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 的泛化,可以为每个终止的真情况指定一个替代结果值。

【讨论】:

    【解决方案2】:

    继续进行代码打高尔夫球,由于预期 tnil,您只能使用 ornil 来表达条件(并使用 ornil 的短路表达式)。

    此外,能够确定: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 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)))))
    

    好吧,我在这里使用某种reverse*

    如果有的话:

    (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
    

    【讨论】:

      【解决方案3】:

      为了完成其他答案,我想指出,不使用 reverse 不仅会使您的代码非常复杂,而且还会使其效率低得多。只需将上述答案与经典答案进行比较即可:

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

      reverse 是 o(l),即它所花费的时间与列表 l 的长度成正比,equal 也是如此。所以这个函数将在 o(l) 中运行。没有比这更快的了。

      【讨论】:

      • 当然,但我们避免使用reverse,因为它是必需的。其次,在他列出的测试中,他可能希望在子列表中进行递归测试......
      猜你喜欢
      • 2019-01-05
      • 2015-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多