Search code examples
schemeracketsicp

How to step through this evaluation?


I would like to see how the value of a square root is iteratively improved. For example on the following:

#lang sicp
(define (square x) (* x x))
(define (average x y) (/ (+ x y) 2))
(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 2)

It gets values such as the following:

1 1
2 1.5
3 1.4166666666666665
4 1.4142156862745097

As an example of what I want to show, in Javascript I would do something like:

const sqrt_iter = (guess, x) => {
    console.log(count++, guess);
    return good_enough(guess, x) ? guess : sqrt_iter(improve(guess, x), x);
}
const sqrt = x => sqrt_iter(1.0, x);

How could I print or trace these intermediate values in DrRacket/SICP? I tried doing (trace sqrt) but it said not found.


Solution

  • I am sure Racket has some fancy trace facility. But there's a famous quote (due I think to John Foderaro):

    Lisp [for which read Racket] is the programmable programming language.

    What this means is: if there's no tracing facility, or you are too lazy to make one, you can just write one.

    Here is a rudimentary one I wrote in five minutes:

    #lang racket
    
    (provide define/traced)
    
    (define trace-depths (make-parameter 0))
    
    (define (spaces n)
      (make-string n #\ ))
    
    (define-syntax define/traced
      (syntax-rules ()
        [(_ (name arg ...) form ...)
         (define/traced name (λ (arg ...) form ...))]
        [(_ (name . args) form ...)
         (define/traced name (λ args form ...))]
        [(_ name function)
         (define name
           (λ args
             (let* ([depth (trace-depths)]
                    [prefix (spaces depth)])
               (parameterize ([trace-depths (+ depth 1)])
                 (printf "~A~S ...~%" prefix `(,'name ,@args))
                 (call-with-values
                  (thunk (apply function args))
                  (λ results
                    (printf "~A-> ~S~%" prefix results)
                   (apply values results)))))))]))
    

    Stash this in a file called define-traced.rkt and then require it, and tell it to trace the procedures you care about:

    #lang racket
    
    (require "define-traced.rkt")
    
    (define (square x) (* x x))
    (define (average x y) (/ (+ x y) 2))
    (define/traced (improve guess x) (average guess (/ x guess)))
    (define (good-enough? guess x) (< (abs (- (square guess) x)) 0.001 ))
    (define/traced (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 2)
    

    Which will duly print this:

    (sqrt-iter 1.0 2) ...
     (improve 1.0 2) ...
     -> (1.5)
     (sqrt-iter 1.5 2) ...
      (improve 1.5 2) ...
      -> (1.4166666666666665)
      (sqrt-iter 1.4166666666666665 2) ...
       (improve 1.4166666666666665 2) ...
       -> (1.4142156862745097)
       (sqrt-iter 1.4142156862745097 2) ...
       -> (1.4142156862745097)
      -> (1.4142156862745097)
     -> (1.4142156862745097)
    -> (1.4142156862745097)
    1.4142156862745097
    

    Note that when I said it was a rudimentary facility I meant it: in particular it will probably turn tail calls into non-tail calls, and there are many other things wrong with it. But it took less long to write than it would take to read the manual on some hairy facility. If I was going to use this thing just once (and this is probably the only time I will ever use it: it only made it into a file so I could require it in another file) it's worth it. This is one of the glories of Lisp-family languages.