Home

SICP - 9 - 赋值和局部状态

7/27/2024

局部状态变量

全局状态变量

(define balance 100)

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

(display (withdraw 50)) (newline)
(display (withdraw 100))

局部状态变量

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

各自独立的局部状态

(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))

(display (W1 50)) (newline)
(display (W2 70)) (newline)
(display (W2 40)) (newline)
(display (W1 40))

消息传递风格

(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 "Unknown request -- MAKE-ACCOUNT" m)]))

  dispatch)

(define acc (make-account 100))

(display ((acc 'withdraw) 50)) (newline)
(display ((acc 'withdraw) 60)) (newline)
(display ((acc 'deposit) 40)) (newline)
(display ((acc 'withdraw) 60))

练习 3.1

累加器

(define (make-accumulator initial)
  (let ([value initial])
    (lambda (x)
      (set! value (+ value x))
      value)))

(define A (make-accumulator 10))

(display (A 10)) (newline)
(display (A 20))

练习 3.2

make-monitored

(define (make-monitored f)
  (let ([counter 0])
    (lambda args
      (if (eq? (car args) 'how-many-calls)
          counter
          (begin (set! counter (+ counter 1))
                 (apply f args))))))

(define s (make-monitored sqrt))

(display (s 100)) (newline)
(display (s 'how-many-calls))

练习 3.3

带密码保护的账户

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

  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)

  (define (dispatch pwd m)
    (cond [(not (eq? pwd password)) (lambda args "Incorrect password")]
          [(eq? m 'withdraw) withdraw]
          [(eq? m 'deposit) deposit]
          [else (error "Unknown request -- MAKE-ACCOUNT" m)]))

  dispatch)

(define acc (make-account 100 'secret-password))

(display ((acc 'secret-password 'withdraw) 40)) (newline)
(display ((acc 'some-other-password 'deposit) 50)) (newline)

引进赋值带来的利益

(define (rand-update x)
  (let ([m (- (expt 2 31) 1)]
        [a 48271]
        [c 0])
    (remainder (+ (* a x) c) m)))

(define rand
  (let ([x 1])
    (lambda ()
      (set! x (rand-update x))
      x)))

(define (gcd a b)
  (if (= b 0)
      a
      (gcd b (remainder a b))))

(define (estimate-pi trials)
  (sqrt (/ 6 (monte-carlo trials cesaro-test))))

(define (cesaro-test)
  (= (gcd (rand) (rand)) 1))

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

(estimate-pi (expt 2 12))

不引入赋值下的实现

(define (rand-update x)
  (let ([m (- (expt 2 31) 1)]
        [a 48271]
        [c 0])
    (remainder (+ (* a x) c) m)))

(define (gcd a b)
  (if (= b 0)
      a
      (gcd b (remainder a b))))

(define (estimate-pi trials)
  (define random-init 1)
  (sqrt (/ 6 (random-gcd-test trials random-init))))

(define (random-gcd-test trials initial-x)
  (define (iter trials-remaining trials-passed x)
    (let* ([x1 (rand-update x)]
           [x2 (rand-update x1)])
      (cond [(= trials-remaining 0)
             (/ trials-passed trials)]
            [(= (gcd x1 x2) 1)
             (iter (- trials-remaining 1)
                   (+ trials-passed 1)
                   x2)]
            [else
             (iter (- trials-remaining 1)
                   trials-passed
                   x2)])))
  (iter trials 0 initial-x))

(estimate-pi (expt 2 12))

练习 3.5

蒙特卡罗积分

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

(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))

(define (estimate-intergral p x1 x2 y1 y2 trials)
  (define ratio
    (monte-carlo
     trials
     (lambda ()
       (let ([x (random-in-range x1 x2)]
             [y (random-in-range y1 y2)])
         (p x y)))))
  (* (- x2 x1) (- y2 y1) ratio))

(define (square x) (* x x))

(- (estimate-intergral
    (lambda (x y)
      (<= (+ (square (- x 5)) (square (- y 7))) 9))
    2.0
    8.0
    4.0
    10.0
    10000)
   (* 3.1415926535 (square 3)))

练习 3.6

可以重置的随机数生成器

(define (rand-update x)
  (let ([m (- (expt 2 31) 1)]
        [a 48271]
        [c 0])
    (remainder (+ (* a x) c) m)))

(define (make-rand initial)
  (let ([x initial])
    (define (rand m . args)
      (cond [(eq? m 'generate) (begin (set! x (rand-update x)) x)]
            [(eq? m 'reset) (set! x (car args))]
            [else (error "Unknown request -- RAND" m)]))
    rand))

(define rand (make-rand 1))

(display (rand 'generate)) (newline)
(display (rand 'generate)) (newline)
(display (rand 'generate)) (newline)
(rand 'reset 1)
(display (rand 'generate)) (newline)
(display (rand 'generate)) (newline)
(display (rand 'generate)) (newline)

引进赋值的代价

练习 3.7 可共用的银行账户对象

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

  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)

  (define (dispatch pwd m)
    (cond [(not (eq? pwd password)) (lambda - "Incorrect password")]
          [(eq? m 'withdraw) withdraw]
          [(eq? m 'deposit) deposit]
          [else (error "Unknown request -- MAKE-ACCOUNT" m)]))

  dispatch)

(define (make-joint acc password new-password)
  (lambda (pwd m)
    (if (not (eq? pwd new-password))
        (lambda - "Incorrect password")
        (acc password m))))

(define peter-acc (make-account 100 'open-sesame))

(define paul-acc
  (make-joint peter-acc 'open-sesame 'rose-bud))

(display ((peter-acc 'open-sesame 'deposit) 20)) (newline)
(display ((paul-acc 'rose-bud 'withdraw) 10)) (newline)
(display ((paul-acc 'open-sesame 'withdraw) 10))
(define (make-f)
  (let ([x -0.5])
    (lambda (y) (begin (set! x (+ x y))
                       x))))

(define f (make-f))
(define arg1 (f 0))
(define arg2 (f 1))

(display (+ arg1 arg2)) (newline)

(set! f (make-f))
(set! arg2 (f 1))
(set! arg1 (f 0))
(display (+ arg1 arg2))