【问题标题】:Returning NIL from a recursion in Lisp从 Lisp 中的递归返回 NIL
【发布时间】:2017-08-18 06:26:38
【问题描述】:

我正在研究一本书中的一个问题,其中我有一棵二叉树,我需要检查左右子树上所有原子的总和是否相等,如果不相等,则返回 nil。我设法在两个函数中做到这一点,但是当我尝试在一个函数中做到这一点时,我得到一个错误,因为它试图将一个数字添加到 nil。

代码是

(defun foo (list)
   (cond ((null list) 0)
         ((atom list) list)
         ((/= (foo (cadr list))
              (foo (caddr list))) nil)
         ( T (+ (foo (car list))
                (foo (cdr list))))))

编辑:问题有两个。

1) 使用之前的结构,它会尝试评估 (cdr '(number)) 所以当它遇到一个看起来像 '(a (bc) d) 的列表时它会返回 null,因为它会尝试访问 (cdr '(d))

2) 我使用 /= 仅当两个参数都是数字时才有效

有效的代码:

(defun foo (list)
  (cond ((null list) 0)
        ((atom list) list)
        ((null (cdr list)) (car list))
        ((null (equal(foo (cadr list)) (foo (caddr list)))) nil)
        (T (+ (car list)
              (foo (cadr list))
              (foo (caddr list))))))

【问题讨论】:

  • 你是怎么调用这个函数的?
  • 您的规格不完整。就像现在写的那样,您可以通过(defun foo (list) nil) 来满足它。
  • 由于只能加数字,所以在使用前需要检查FOO的结果。
  • @RainerJoswig 我认为问题不在于添加数字,我认为问题在于我的逻辑。理想情况下,它不应该添加 nil 和数字,因为它应该以 nil 退出,并且具有两个函数的实现可以工作,因为它下降到树的有限级别。
  • @melpomene 我认为你是对的,我似乎无法弄清楚如何让它工作或我应该添加什么。

标签: lisp


【解决方案1】:

有效的代码:

(defun foo (list)
  (cond ((null list) 0)
        ((atom list) list)
        ((null (cdr list)) (car list))
        ((null (equal(foo (cadr list)) (foo (caddr list)))) nil)
        (T (+ (car list)
              (foo (cadr list))
              (foo (caddr list))))))

【讨论】:

    【解决方案2】:

    注意:我假设您的二叉树是 (val left-sub right-sub) 的列表,这似乎与您的代码匹配。

    我不确定是否有一种使用递归和单个函数的简洁方法,因为递归过程(对左右子树求和)与您的函数需要返回的值不同(无论是否左右子树相等)。

    但是,如果你绝对必须用一个函数来解决它,你可能会作弊。我看到两个选项:

    选项 1

    局部函数

    (defun foo (list)
      (labels ((sum-subtrees (list)
                 (cond
                   ((null list) 0)
                   ((atom list) list)
                   (t (+ (car list)
                         (sum-subtrees (cadr list))
                         (sum-subtrees (caddr list)))))))
        (= (sum-subtrees (cadr list))
           (sum-subtrees (caddr list)))))
    

    这通过定义一个本地函数来处理递归位 - #'sum-subtrees- 然后仅依靠它来计算最终输出。

    选项 2

    多值返回

    (defun foo (list)
      (cond
        ((null list) (values t 0))
        ((atom list) (values t list))
        (t (let ((left-sub (nth-value 1 (foo (cadr list))))
                 (right-sub (nth-value 1 (foo (caddr list)))))
             (values (= left-sub right-sub)
                     (+ (car list)
                        left-sub
                        right-sub))))))
    

    此解决方案利用了常见的 lisp 函数如何返回多个值。基本上,该函数返回原始条件(= left-subtree right-subtree) 树的总和。任何其他只需要一个值的代码都将获得第一个返回值(条件),因此任何使用此函数的代码都不应注意到额外的返回值,但如果您要求,数据就在那里。

    我们返回多个值的方式是使用 values 函数。以这段代码为例,我们在list为nil的情况下返回(values t 0),表示其“左右子树”相等且和为0,

    (values (= left-sub right-sub)
            (+ (car list)
               left-sub
               right-sub))
    

    产生递归返回值。

    有几种方法可以访问额外的返回值,但这里使用的是#'nth-value,它返回第n 个返回的值而不是第一个。这就是为什么当我们进行递归调用来计算子树的大小时,我们使用(nth-value 1 (foo <subtree>))

    注意:请永远不要实际使用该解决方案,多值返回非常有用且功能强大,但在这种情况下,它比它真正的价值更令人困惑。

    【讨论】:

    • 您的第一个解决方案检查右子树和左子树是否相等,这不是问题所在,例如 '(6 (4 (2 1 1) 4) (2当最后一个列表 '(1 1 3) 不平衡时,5 (1 1 3))) 将返回 true。该代码是我第一次尝试的样子。您的代码还无法处理其他两个因素。 1) 没有更多的子树,'(4) 会调用 cadr 和 caddr 并且会返回错误,因为您将 nil 添加到先前递归的结果中。 2)“=”只作用于我遇到的问题
    • 我认为问题在于检查给定树左子树上的原子总和是否等于我的代码计算的该树右子树上的原子总和。此外,调用(cdr nil) 可以正常工作——它返回 nil,这是我的代码所期望并处理的。注意,我调用(sum-subtrees (cadr '(4))),它返回0,因为#'sum-subtrees 在传递nil 时返回0。至于#'=left-subright-sub 永远不会为零——如果没有左/右子树,它们将是 0。
    【解决方案3】:

    在定义了如何表示二叉树之后,我的意思是右子树可以是 cdr 或 cadr,我将这两个问题分开:

    (defun sum-of-subtree (tree)
       (cond ((null tree) 0)
          ((atom tree) tree)
          (t (+ (sum-of-subtree (car tree))
                (sum-of-subtree (cdr tree))))))
    (defun foo (tree)
       (cond ((null tree) t) ;or whatever you want
             ((atom tree) t)
             ((= (sum-of-subtree (car tree))
                 (sum-of-subtree (cdr tree))) t)
             (t nil)))
    

    这样,您不会将子树之和的值与比较混淆。其他语言的打字比较强,避免了不同功能的用途混在一起

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 2015-09-07
      • 2021-09-20
      • 2014-11-15
      相关资源
      最近更新 更多