Search code examples
lispelispsicp

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


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?


Solution

  • 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)))