Search code examples
recursionschemelispsicp

How to add a counter to this scheme function


I would like to add a counter so I can see how many times the iteration runs:

(define tolerance 0.01)
(define (close-enough? x y) (< (abs (- x y)) 0.001))

(define (fixed-point function starting-guess)
  (define iter-count 0)
  (define (evaluate num)
    ; these lines increment the counter using set!
    (set! iter-count (+ iter-count 1))
    (display iter-count) (display " - ") (display num) (display "\n")
    (let ((next-num (function num)))
      (if (close-enough? num next-num)
          next-num
          (evaluate next-num))))
  (evaluate starting-guess))

(fixed-point cos 1.0)

What would be the proper way to do this? Currently I have added in a define and a set! as I couldn't figure out a way to get let to work. Is there a way to do this with let, or what's the suggested way to do this?

Or, I suppose another way is to pass it as a parameter to the iteration function itself:

(define (fixed-point function starting-guess)
  (define (evaluate num iteration-num)
    (display iteration-num) (display " - ") (display num) (display "\n")
    (let ((next-num (function num)))
      (if (close-enough? num next-num)
          next-num
          (evaluate next-num (+ 1 iteration-num)))))
  (evaluate starting-guess 0))

Solution

  • Just like num you just add it a a parameter to your loop function:

    (define (fixed-point function starting-guess)
      ;; prints progress
      (define (print-progress iter-count)
        (display iter-count)
        (display " - ") 
        (display num)
        (newline)) 
    
      ;; main calculating loop 
      (define (evaluate num iter-count)
        (print-progress iter-count)
        (let ((next-num (function num)))
          (if (close-enough? num next-num)
              next-num
              (evaluate next-num (+ iter-count 1)))))
    
      ;; start process with iter-count 1 since 
      ;; we do increments after display
      (evaluate starting-guess 1))
    

    Notice that your version of this started displaying 0 while your set! version started with 1. I compensated for this by starting off with 1 instead of 0.

    You could keep the side effects away from fixed-point completely by adding the functionality to the function:

    ;; pure functional fixed-point
    (define (fixed-point function starting-guess)
      (define (evaluate num)
        (let ((next-num (function num)))
          (if (close-enough? num next-num)
              next-num
              (evaluate next-num))))
      (evaluate starting-guess))
        
    ;; makes a version of function that 
    ;; reports its first argument and 
    ;; number of times it's been called
    (define (count-and-brag-calls f)
      ;; brag does whatever and
      ;; return the value
      (define (brag v c)
        (display c)
        (display " - ") 
        (display v)
        (newline)
        v) 
    
      ;; actual implementation
      (let ((count 0))
        (lambda (n)
          (set! count (+ count 1))
          (brag (f n) count))))
    
    ;; with verbose output
    (fixed-point (count-and-brag-calls cos) 1.0)
    
    ;; without side effects gives exact same result without output
    (fixed-point cos 1.0)