【问题标题】:Lisp Infinite RecursionLisp 无限递归
【发布时间】:2014-11-26 14:36:54
【问题描述】:

我是一个新的 lisp 程序员,我无法理解 lisp 中的递归。我有一系列表达式,我通过一系列用数字替换符号的方法来简化它们,然后我将评估表达式。在评估之前,我用符号代替数字,这样做我在我的 subst-bindings 方法中和/或当我从该方法中调用 deep-subst 方法时出现堆栈溢出错误。关于我的递归方法调用的任何帮助或建议可以帮助我更好地理解,我将不胜感激!我的代码如下--

    (setq p1 '(+ x (* x (- y (/z 2)))))
    (setq p2 '(+ (- z 2) (* x 5)))
    (setq p3 '(+ 1 a))


    (defun deep-subst (old new l)
      (cond
       ((null l) 
         nil
       )
       ((listp (car l))
        (cons (deep-subst old new (car l)) (deep-subst old new (cdr l)))
       )
       ((eq old (car l)) 
        (cons new (deep-subst old new (cdr l)))
       )
       (T   
        (cons (car l) (deep-subst old new (cdr l)))
       )
      )
    )

    (defun subst-bindings (bindinglist exp)
      (cond 
        ( (null bindinglist) 
           exp )
        (T
           (subst-bindings  (cdr bindinglist)(deep-subst (car (car bindinglist)) (cdr (car bindinglist)) exp))
        )
       )
     )

    (defun simplify (exp)
        (cond
         ( (listp exp)
          (simplify-triple (car exp) (simplify (car(cdr exp)))(simplify (car (cdr (cdr exp)))))
        (T
           exp))))

    (defun evalexp (binding-list exp)
        (simplify (subst-bindings binding-list exp))
    )
      (evalexp '( (x 2) (z 8) ) p1)  ;Where I call evalexp from and gives me the stack overflow error

【问题讨论】:

  • 如果您可以仅显示最少量的代码和重现问题的测试用例以及您收到的错误消息,将会更有帮助。 Common Lisp 还包含一个跟踪宏,因此如果您要执行 (trace deep-subst),您将看到所有对 deep-subst 的调用,您可能会发现问题。
  • 我会试一试,我还取出了对发现问题没有必要或无帮助的代码。最后一行代码是我调用 evalexp 的示例测试用例
  • 我想你可能想调试更多。问题似乎并非出现在替换函数中。例如,pastebin.com/NWHPK5PF 运行没有问题(尽管结果可能不是您期望的那样;绑定列表应该是'((x . 2) (z . 8)) 吗?)。这表明simplify 有问题。
  • 嗯,很有趣,我没想过要把它全部删掉并单独测试,我相信你是对的,我需要仔细看看简化。
  • @Branbron 我会提到一件事;当我运行我在之前的评论中链接到的示例时,我注意到您的替换算法采用(a . b) 形式的绑定来将 a 替换为 b。在您使用的绑定中,您将用 (2) 替换 x,用 (8) 替换 z。其中每一个都是一个列表,因此您可以递归更多的结构。在那里犯错不会太难。

标签: recursion lisp common-lisp


【解决方案1】:

问题在于作为跟踪证明的简化函数

(trace simplify)

(evalexp '( (x 2) (z 8) ) p1)
0: (SIMPLIFY (+ (2) (* (2) (- Y (/Z 2)))))
    1: (SIMPLIFY (2))
      2: (SIMPLIFY NIL)
        3: (SIMPLIFY NIL)
          4: (SIMPLIFY NIL)
            5: (SIMPLIFY NIL)
              6: (SIMPLIFY NIL)
                7: (SIMPLIFY NIL)
                  8: (SIMPLIFY NIL)
                    9: (SIMPLIFY NIL)
                      10: (SIMPLIFY NIL)
                        11: (SIMPLIFY NIL)
                          12: (SIMPLIFY NIL)
                            13: (SIMPLIFY NIL)
                              14: (SIMPLIFY NIL)
                                15: (SIMPLIFY NIL)
                                  16: (SIMPLIFY NIL)
                                    17: (SIMPLIFY NIL)
                                      18: (SIMPLIFY NIL)

如果我们看一下函数

(defun simplify (exp)
        (cond
          ((listp exp)
           (simplify-triple 
              (car exp) 
              (simplify (car(cdr exp)))
              (simplify (car (cdr (cdr exp)))))
          (T
            exp))))

我们可以看到递归是基于函数listp。如果listp 返回true,那么simplify-triple 将被调用,其中两次调用simplify 作为参数。正如我们在跟踪中看到的那样,simplify 一遍又一遍地用nil 调用,如果我们测试(listp nil),我们可以看到它返回T(这使得sense 代表empty list)因此导致无限递归。

您必须将递归建立在另一个(或更多)if 条件上。

【讨论】:

  • 我会尝试用不同的方式检查返回值,谢谢你帮助我理解!
  • 您可以使用(and (listp exp) exp) 之类的东西来确保您不会使用nil,因为and 会将nil 解释为布尔值false
  • 测试列表末尾的常用方法是使用endp
猜你喜欢
  • 2011-02-28
  • 2015-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多