【问题标题】:max-lisp-eval-depth to find sqrt-itermax-lisp-eval-depth 找到 sqrt-iter
【发布时间】:2019-11-17 03:20:26
【问题描述】:

我正在研究 SICP 的练习 1.6,它重写了演示案例

#+begin_src emacs-lisp :session sicp :results output
(defun sqrt(x)
  (sqrt-iter 1.0 x)) 

(defun sqrt-iter(guess x)
  (if (good-enough-p guess x)
      guess
      (sqrt-iter (improve guess x)
                 x)))

(defun good-enough-p(guess x)
  (< (abs (- (square guess) x)) 0.001))

(defun improve(guess x)
  (average guess (/ x guess)))

(defun average(x y)
  (/ (+ x y) 2))
#+end_src

它可以工作并获得输出

#+begin_src emacs-lisp :session sicp :lexical t :results output
(print (sqrt 11))
(print (sqrt (+ 100 37)))
(print (sqrt (+ (sqrt 2) (sqrt 3))))
#+end_src

#+RESULTS:
: 
: 3.3166248052315686
: 
: 11.704699917758145
: 
: 1.7739279023207892

因此来到练习 1.6,用cond 重写它

#+begin_src emacs-lisp :session sicp :lexical t
(defun sqrt-iter-cond(guess x)
  (cond ((good-enough-p guess x) guess)
        (t (sqrt-iter-cond (improve guess x) x))
   )
  )
(sqrt-iter-cond 1 10)
#+end_src

它报告错误:

  ob-async-org-babel-execute-src-block: Lisp nesting exceeds ‘max-lisp-eval-depth’

在阅读了各种解释后,我陷入了更多的困惑,甚至产生了一种隐含的恐惧来使用cond 后记。因为这看起来逻辑上显然是正确的。

能否给点提示?

【问题讨论】:

  • 只是出于好奇:您为什么要使用 Elisp 解决 SICP 的练习?这会让你的生活更艰难,这本书是为了使用 Scheme。
  • 在认真阅读《魔法书》之前,我意识到自己对编程知之甚少,关于思考“在事物上发挥精神力量”。然而,scheme 可能是一个在原型中组织我的想法的工具,它停在那里,这将禁止我长期使用它作为思考的习惯,因为scheme 不能真正使用。尝试 elisp 可以保留我从 scp 学到的思考习惯。 @ÓscarLópez
  • @Algebra “方案不能真正使用” - 不。这当然是我的意见,但。有人告诉你一个谎言,大谎言。一年多以来,我在所有项目中都使用 Scheme,但仍然需要找到使用 Scheme 无法解决的问题。

标签: lisp elisp sicp


【解决方案1】:

cond 版本没什么问题,先从空缓冲区开始试试这个,就可以了:

(defun sqrt (x)
  (sqrt-iter-cond 1.0 x))

(defun sqrt-iter-cond (guess x)
  (cond ((good-enough-p guess x) guess)
        (t (sqrt-iter-cond (improve guess x) x))))

(defun good-enough-p (guess x)
  (< (abs (- (square guess) x)) 0.001))

(defun improve (guess x)
  (average guess (/ x guess)))

(defun average(x y)
  (/ (+ x y) 2))

(print (sqrt 11))
=> 3.3166247903554

但是这个练习不是关于用cond 重写过程,它应该告诉你不能使用过程编写你自己的if 版本,并且你需要一个带有特殊评估规则的特殊表格 -因为评估程序规则将同时评估结果部分和替代部分,这是行不通的!看看这个简单的例子,明白我的意思:

(if t 'ok (/ 1 0))

上面将返回'ok,即使那里有一个被零除:那部分永远不会被执行。但是,如果我们尝试将自己的if 实现为正常过程,它将失败并出现除以零错误:

(defun my-if (condition consequent alternative)
  (cond (condition consequent)
        (t alternative)))

(my-if t 'ok (/ 1 0))

现在回到您的代码并尝试一下,当然它也会失败,但这次出现无限递归错误(这就是“Lisp 嵌套超过 'max-lisp-eval-depth'”消息的含义) :

(defun sqrt-iter (guess x)
  (my-if (good-enough-p guess x)
         guess
         (sqrt-iter (improve guess x)
                    x)))

【讨论】:

    【解决方案2】:

    sqrt-iter 之所以如此命名是因为它是尾递归的。它旨在由将其编译为迭代的 Scheme 实现进行处理。 Emacs Lisp 不是尾递归的,因此代码执行实际的递归。它产生了如此多的功能激活,以至于达到了深度限制。

    我们能做的就是将其重写为显式迭代:

    (defun sqrt-iter(guess x)
      (while (not (good-enough-p guess x))
        (setq guess (improve guess x)))
      guess))
    

    另一个想法是尝试使用这个 Elisp tail call module 提供的 defun-tco 宏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-16
      • 1970-01-01
      • 2020-04-20
      相关资源
      最近更新 更多