sicp-ex-3.6



<< Previous exercise (3.5) | Index | Next exercise (3.7) >>


 (define rand 
   (let ((x random-init)) 
     (define (dispatch message) 
       (cond ((eq? message 'generate) 
               (begin (set! x (rand-update x)) 
                      x)) 
             ((eq? message 'reset) 
               (lambda (new-value) (set! x new-value))))) 
     dispatch)) 

Test:

 (define random-init 0) 
 (define (rand-update x) (+ x 1)) ; A not-very-evolved PNRG 
 (rand 'generate) 
 ; 1 
 (rand 'generate) 
 ; 2 
 ((rand 'reset) 0) 
 ; 0 
 (rand 'generate) 
 ; 1 

It's interesting to notice that the lambda returned by a call to (rand 'reset) still has the closure we created as lexical scope:

 x 
 ; Error: undefined variable 'x'. 

hi-artem

Here, inside (cond ..) construct, using "begin" is unnecessary.


Shawn

We could also define rand as a procedure instead of a variable:

  
 (define (rand arg) 
   (let ((x random-init)) 
     (cond ((eq? arg 'generate) 
            (set! x (rand-update x)) 
            x) 
           ((eq? arg 'reset) 
            (lambda (new-value) (set! x new-value)))))) 

pa3

Shawn's version will not work actually. It will always return the result of evaluating (rand-update random-input), because the state (x variable) is being recreated and reset to random-input each time rand function invoked.


joew

 #lang sicp 
 ;"random" number generator 
 (define (rand-update x) 
   (let ((a 27) (b 26) (m 127)) 
     (modulo (+ (* a x) b) m))) 
  
 (define x1 (rand-update 0)) 
 (define x2 (rand-update x1)) 
 (define x3 (rand-update x2)) 
 (define x4 (rand-update x3)) 
 (define x5 (rand-update x4)) 
 x1 
 x2 
 x3 
 x4 
 x5 
 ;function that returns a closure 
 ;closures seem to be the way to initialize objects with state in scheme 
 (define (r) 
   (let ((seed 0)) 
     (define (dispatch m) 
       (cond ((eq? m 'reset) (lambda (x)(set! seed x))) 
             ((eq? m 'generate) (begin (set! seed (rand-update seed)) 
                                       seed)) 
             (else error "invalid operation"))) 
     dispatch)) 
 ;define rand in terms of r and the closure it returns 
 (define rand (r)) 
 ;test that rand works first with no args assuming 0 as seed 
 (rand 'generate) 
 (rand 'generate) 
 (rand 'generate) 
 ;test that pattern reoccurs 
 ((rand 'reset) 0) 
 (rand 'generate) 
 (rand 'generate) 
 (rand 'generate) 
 ;test the rand works using a new seed 
 ((rand 'reset) 42) 
 (rand 'generate) 
 (rand 'generate) 
 (rand 'generate) 
 ;test the pattern reoccurs 
 ((rand 'reset) 42) 
 (rand 'generate) 
 (rand 'generate) 
 (rand 'generate) 

codybartfast

Just for the laughs here are two (functionally) identical implementations, one using define to create named procedures:

(define (make-rand)
  (define x 0)
  (define (set-x! new-x)
    (set! x new-x))
  (define (dispatch message)
    (cond
      ((eq? message 'generate)
       (set! x (rand-update x))
       x)
      ((eq? message 'reset)
       set-x!)
      (else ((error "Unknown Message - " message)))))
  dispatch)

and the other using lambda to create anonymous procedures:

(define (make-rand)
  (let ((x 0))
    (lambda (message)
      (cond
        ((eq? message 'generate)
         (set! x (rand-update x))
         x)
        ((eq? message 'reset)
         (lambda (new-x) (set! x new-x)))
        (else ((error "Unknown Message - " message)))))))

Usage:

(define (make-rand)
  ...)

(define my-rand (make-rand))

((my-rand 'reset) 42)
(my-rand 'generate)
(my-rand 'generate)