max-lisp-eval-depth to find sqrt-iter

北慕城南 提交于 2020-01-16 11:12:10

问题


I am working on SICP's exercise 1.6 which rewrite the demonstration case

#+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

It works and get the output

#+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

Thus come to Exercise 1.6 which rewrite it with 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

It report errors:

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

After reading various explanations, more confusions I immersed in and even raise an implicit fear to employ cond afterwords. Because It seem clearly logical correct.

Could please give any hints?


回答1:


There's nothing wrong with the cond version, start with an empty buffer and try this, it'll work:

(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

But the exercise is not about rewriting the procedure with cond, it's supposed to show you that you can't write your own version of if using procedures, and that you need a special form with special evaluation rules - because evaluation rules of procedures will evaluate both the consequent and the alternative parts at the same time, and that won't work! Look at this simple example to see what I mean:

(if t 'ok (/ 1 0))

The above will return 'ok, even though there's a division by zero there: that part never got executed. But if we try to do it with our own if implemented as a normal procedure, it'll fail with a division by zero error:

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

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

Now go back to your code and try it, of course it'll also fail, but this time with an infinite recursion error (that's what the "Lisp nesting exceeds ‘max-lisp-eval-depth’" message means):

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



回答2:


sqrt-iter is so named because it's tail recursive. It's intended to be processed by a Scheme implementation which will compile it to iteration. Emacs Lisp isn't tail recursive, and so the code performs actual recursion. It's generating so many function activations that it hits the depth limit.

What we can do is rewrite it to explicit iteration:

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

Another idea is to try using the defun-tco macro provided by this Elisp tail call module.



来源:https://stackoverflow.com/questions/58897212/max-lisp-eval-depth-to-find-sqrt-iter

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!