SICP Study

3.1 Assignment and Local State

3.1.1 Local State Variables

(define balance 100)
(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
      "Insufficient funds"))

(withdraw 25) => 75
(withdraw 25) => 50
(withdraw 60) => "Insufficient funds"
(withdraw 15) => 35

(define new-withdraw
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

(new-withdraw 25) => 75
(new-withdraw 25) => 50
(new-withdraw 60) => "Insufficient funds"
(new-withdraw 15) => 35

(define (make-withdraw balance)
  (lambda (amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds")))

(define W1 (make-withdraw 100))
(define W2 (make-withdraw 100))
(W1 50) => 50
(W2 70) => 30
(W2 40) => "Insufficient funds"
(W1 40) => 10

(define (make-account balance)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch m)
    (cond ((eq? m 'withdraw) withdraw)
          ((eq? m 'deposit) deposit)
          (else (error 'make-account "unknown request" m))))
  dispatch)

(define acc (make-account 100))
((acc 'withdraw) 50) => 50
((acc 'withdraw) 60) => "Insufficient funds"
((acc 'deposit) 40) => 90
((acc 'withdraw) 60) => 30
((acc 'floof)) =!> "unknown request: floof"

Exercise 3.1

(define (make-accumulator amount)
  (lambda (increment)
    (set! amount (+ amount increment))
    amount))

(define A (make-accumulator 5))
(A 10) => 15
(A 10) => 25

Exercise 3.2

(define (make-monitored f)
  (let ((n-calls 0))
    (lambda (x)
      (cond ((eq? x 'how-many-calls?) n-calls)
            ((eq? x 'reset-count) (set! n-calls 0))
            (else (set! n-calls (+ n-calls 1))
                  (f x))))))

(define s (make-monitored sqrt))
(s 100) => 10
(s 'how-many-calls?) => 1
(s 25) => 5
(s 'how-many-calls?) => 2
(s 'reset-count)
(s 'how-many-calls?) => 0

Exercise 3.3

(define (make-account balance password)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch p m)
    (if (eq? p password)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
              (else (error 'make-account "unknown request" m)))
        (lambda (x) "Incorrect password")))
  dispatch)

(define acc (make-account 100 'secret-password))
((acc 'secret-password 'withdraw) 40) => 60
((acc 'some-other-password 'deposit) 50) => "Incorrect password"

Exercise 3.4

(define (make-account balance password)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (let ((consecutive-wrong 0))
    (define (dispatch p m)
      (if (eq? p password)
          (cond ((eq? m 'withdraw) withdraw)
                ((eq? m 'deposit) deposit)
                (else (error 'make-account "unknown request" m)))
          (lambda (x)
            (set! consecutive-wrong (+ consecutive-wrong 1))
            (if (> consecutive-wrong 7)
                (call-the-cops)
                "Incorrect password"))))
    dispatch))

(define (call-the-cops) "Calling the cops!")

(define acc (make-account 'right 100))
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Incorrect password"
((acc 'wrong 'withdraw) 100) => "Calling the cops!"

3.1.2 The Benefits of Introducing Assignment

Tausworthe PRNG: https://stackoverflow.com/a/23875298

(define random-init 1)
(define random-max #x7fffffff)
(define (rand-update x0)
  (let* ((x1 (fxxor x0 (fxarithmetic-shift-right x0 13)))
         (x2 (fxxor x1 (fxarithmetic-shift-left x1 18))))
    (fxand x2 random-max)))

(define rand
  (let ((x random-init))
    (lambda ()
      (set! x (rand-update x))
      x)))

(define (estimate-pi trials)
  (sqrt (/ 6 (monte-carlo trials cesaro-test))))
(define (cesaro-test)
  (= (gcd (rand) (rand)) 1))

(define (monte-carlo trials experiment)
  (define (iter trials-remaining trials-passed)
    (cond ((= trials-remaining 0)
           (/ trials-passed trials))
          ((experiment)
           (iter (- trials-remaining 1)
                 (+ trials-passed 1)))
          (else (iter (- trials-remaining 1)
                      trials-passed))))
  (iter trials 0))

This is deterministic because the random-init seed is fixed.

(estimate-pi 1000) ~> 3.149183286488868

Exercise 3.5

(define (random-in-range low high)
  (let ((range (- high low)))
    (+ low (random range))))

(define (estimate-integral pred x1 x2 y1 y2 trials)
  (let ((test (lambda ()
                (pred (random-in-range x1 x2)
                      (random-in-range y1 y2)))))
    (* (monte-carlo trials test)
       (- x2 x1)
       (- y2 y1))))

(define (estimate-pi trials)
  (let ((pred (lambda (x y)
                (<= (+ (square x) (square y)) 1))))
    (estimate-integral pred -1.0 1.0 -1.0 1.0 trials)))

Exercise 3.6

(define rand
  (let ((x random-init))
    (lambda (message)
      (cond ((eq? message 'generate)
             (set! x (rand-update x))
             x)
            ((eq? message 'reset)
             (lambda (new-x)
               (set! x new-x)))
            (else (error 'rand "message not recognized" message))))))

(number? (rand 'generate)) => #t
(rand 'reset)
(number? (rand 'generate)) => #t

3.1.3 The Costs of Introducing Assignment

(define (make-simplified-withdraw balance)
  (lambda (amount)
    (set! balance (- balance amount))
    balance))
(define W (make-simplified-withdraw 25))
(W 20) => 5
(W 10) => -5

(define (make-decrementer balance)
  (lambda (amount)
    (- balance amount)))
(define D (make-decrementer 25))
(D 20) => 5
(D 10) => 15

Substitution analysis of make-decrementer:

((make-decrementer 25) 20)
=> ((lambda (amount) (- 25 amount)) 20)
=> (- 25 20)
=> 5

Faulty substitution analysis of make-simplified-withdraw:

(define balance)
((make-simplified-withdraw 25) 20) => 5
; =>
((lambda (amount) (set! balance (- 25 amount)) 25) 20)
=> (begin (set! balance (- 25 20)) 25)
=> 25

3.1.3.1 Sameness and change

D1 and D2 are the same.

(define D1 (make-decrementer 25))
(define D2 (make-decrementer 25))
(D1 20) => (D2 20) => (D1 20) => (D2 20)

W1 and W2 are surely not the same.

(define W1 (make-simplified-withdraw 25))
(define W2 (make-simplified-withdraw 25))
(W1 20) => 5
(W1 20) => -15
(W2 20) => 5

Distinct accounts:

(define peter-acc (make-account 100))
(define paul-acc (make-account 100))
((peter-acc 'withdraw) 25) => 75
((paul-acc 'withdraw) 25) => 75

Joint account:

(define peter-acc (make-account 100))
(define paul-acc peter-acc)
((peter-acc 'withdraw) 25) => 75
((paul-acc 'withdraw) 25) => 50

3.1.3.2 Pitfalls of imperative programming

(define (imperative-factorial n)
  (let ((product 1)
        (counter 1))
    (define (iter)
      (if (> counter n)
          product
          (begin (set! product (* counter product))
                 (set! counter (+ counter 1))
                 (iter))))
    (iter)))

(imperative-factorial 10) => (factorial 10)

Exercise 3.7

(define (make-joint pp-acc password new-password)
  (lambda (p m)
    (pp-acc (if (eq? p new-password) password #f) m)))

(define peter-acc (make-account 100 'open-sesame))
(define paul-acc (make-joint peter-acc 'open-sesame 'rosebud))
((peter-acc 'rosebud 'withdraw) 10) => "Incorrect password"
((paul-acc 'open-sesame 'withdraw) 10) => "Incorrect password"
((peter-acc 'open-sesame 'withdraw) 10) => 90
((paul-acc 'rosebud 'withdraw) 10) => 80
((peter-acc 'open-sesame 'withdraw) 10) => 70

Exercise 3.8

(define f
  (let ((x 0))
    (lambda (y)
      (let ((old-x x))
        (set! x y)
        old-x))))

(let ((result (+ (f 0) (f 1))))
  (or (= result 0) (= result 1)))
=> #t