# 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