sicp-ex-1.6


The default if statement is a special form which means that even when an interpreter follows applicative substitution, it only evaluates one of it's parameters- not both. However, the newly created new-if doesn't has this property and hence, it never stops calling itself due to the third parameter passed to it in sqrt-iter.


<< Previous exercise (1.5) | sicp-solutions | Next exercise (1.7) >>

jsdalton

I believe this solution is incorrect.

new-if does not use normal order evaluation, it uses applicative order evaluation. That is, the interpreter first evaluates the operator and operands and then applies the resulting procedure to the resulting arguments. As with Excercise 1.5, this results in an infinite recursion because the else-clause is always evaluated, thus calling the procedure again ad infinitum.

The if statement is a special form and behaves differently. if first evalutes the predictate, and then evaluates either the consequent (if the predicate evalutes to #t) or the alternative (if the predicate evalues to #f). This is key difference from new-if -- only one of the two consequent expressions get evaluated when using if, while both of the consequent expressions get evaluated with new-if.

A lenghtier explanation of Applicative Order and Normal Order is here: http://mitpress.mit.edu/sicp/full-text/sicp/book/node85.html



andersc

I agree with jsdalton. The reason why new-if runs out of memory is applicative order evaluation, so if the plain-old if uses applicative order evaluation, it should not work either.

And I guess for a certain interpreter, maybe it should use a consistent way for all processes?


emmp

I believe the above two posters are right and the given answer is wrong.

It's stated clearly in the text that:

"Lisp uses applicative-order evaluation, partly because of the additional efficiency obtained from avoiding multiple evaluations of expressions such as those illustrated with (+ 5 1) and (* 5 2) above and, more significantly, because normal-order evaluation becomes much more complicated to deal with when we leave the realm of procedures that can be modeled by substitution."

So I don't see a reason why MIT-Scheme (which is supposedly what readers of the book use) would be any different. Plus, as andersc wrote, an interpreter would have to be consistent about the evaluation strategy it uses.

As jsdalton said. new-if is a procedure, not a special-form, which means that all sub-expressions are evaluated before new-if is applied to the values of the operands. That includes sqrt-iter which is extended to new-if which again leads to the evaluation of all the sub-expressions including sqrt-iter etc. Instead, in if only one of the consequent expressions is evaluated each time.


dpchrist

new-if works on my machine.

Here's my code:

2013-12-05 21:15:18 dpchrist@desktop ~/sandbox/mit-scheme/sicp2
$ cat ex-1.6.scm | grep -v ';'

(define (average x y) (/ (+ x y) 2))
(define (square x) (* x x))
(define (improve guess x) (average guess (/ x guess)))
(define (good-enough? guess x) (< (abs (- (square guess) x)) 0.001))
(define (sqrt-iter guess x)
  (if (good-enough? guess x)
      guess
      (sqrt-iter (improve guess x) x)))
(define (sqrt x) (sqrt-iter 1.0 x))
(sqrt 9)
(sqrt (+ 100 37))
(sqrt (+ (sqrt 2) (sqrt 3)))
(square (sqrt 1000))

(define (new-if predicate then-clause else-clause)
  (cond (predicate then-clause)
        (else else-clause)))
(new-if (= 2 3) 0 5)
(new-if (= 1 1) 0 5)
(define (new-sqrt-iter guess x)
  (new-if (good-enough? guess x)
          guess
          (sqrt-iter (improve guess x) x)))
(define (new-sqrt x) (new-sqrt-iter 1.0 x))
(if (= (    sqrt 9)
       (new-sqrt 9))
    1 0)
(if (= (    sqrt (+ 100 37))
       (new-sqrt (+ 100 37)))
    1 0)
(if (= (    sqrt (+ (    sqrt 2) (    sqrt 3)))
       (new-sqrt (+ (new-sqrt 2) (new-sqrt 3))))
    1 0)
(if (= (square (    sqrt 1000))
       (square (new-sqrt 1000)))
    1 0)

Here's a sample run on Debian 7.2:

2013-12-05 21:17:24 dpchrist@desktop ~/sandbox/mit-scheme/sicp2
$ cat ex-1.6.scm | grep -v ';' | mit-scheme -eval
MIT/GNU Scheme running under GNU/Linux
Type `^C' (control-C) followed by `H' to obtain information about interrupts.

Copyright (C) 2011 Massachusetts Institute of Technology
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Image saved on Saturday October 15, 2011 at 10:11:41 PM
  Release 9.1 || Microcode 15.3 || Runtime 15.7 || SF 4.41 || LIAR/i386 4.118
  Edwin 3.116

1 ]=> 
;Value: average

1 ]=> 
;Value: square

1 ]=> 
;Value: improve

1 ]=> 
;Value: good-enough?

1 ]=> 
;Value: sqrt-iter

1 ]=> 
;Value: sqrt

1 ]=> 
;Value: 3.00009155413138

1 ]=> 
;Value: 11.704699917758145

1 ]=> 
;Value: 1.7739279023207892

1 ]=> 
;Value: 1000.000369924366

1 ]=> 
;Value: new-if

1 ]=> 
;Value: 5

1 ]=> 
;Value: 0

1 ]=> 
;Value: new-sqrt-iter

1 ]=> 
;Value: new-sqrt

1 ]=> 
;Value: 1

1 ]=> 
;Value: 1

1 ]=> 
;Value: 1

1 ]=> 
;Value: 1

1 ]=> 
End of input stream reached.
Moriturus te saluto.

The poster above does not define new-sqrt-iter as recursive, as it calls the original sqrt-iter instead of itself.