首页 > 解决方案 > SICP 3.6 - 兰德过程和局部状态变量

问题描述

我在 SICP 中的练习 3.6 中遇到困难。他们为伪随机数生成器提供了以下代码:

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

出于测试目的,我添加了:

(define (rand-update x) (+ x 1))
(define random-init 4)

重复申请产生

> (rand)
5
> (rand)
6
> (rand)
7

正如所希望的那样,虽然我不明白为什么会这样。无论如何,练习 3.6 要求我们进行修改rand,使其接受一个参数,指示它为'generateor 'reset

首先,我尝试设置一个带有会产生条件的 rand。然而,我在第一个障碍上绊倒了。

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

给我

> ((rand-new 'generate))
5
> ((rand-new 'generate))
5
> ((rand-new 'generate))
5

就像将 let 表达式移动到条件中一样,如下所示:

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

那么为什么第一个功能有效,而新功能无效呢?和使用条件有关吗?或者添加一个参数?

更新 (19/03/20)

阅读关于计算环境模型的下一节(第 3.2 节)给了我必要的理论来弄清楚发生了什么。我结束了

(define (random-number-generator initial update)
  (define (generate)
    (begin (set! initial (update initial))
          initial))
  (define (reset new-value)
    (begin (set! initial new-value)
           initial))
  (define (dispatch message)
    (cond ((eq? message 'generate) (generate))
          ((eq? message 'reset) reset)
          (else "Procedure not found!")))
  dispatch)

(define rand (random-number-generator 5 rand-update))

标签: functional-programmingschemesicp

解决方案


理解第一个版本为什么有效(以及为什么另一个不有效)的关键在于前三行:

(define rand
  (let ((x random-init))
    (lambda ()

如您所见,名称rand被分配给- 但在此之前,会在之外的范围内创建lambda一个变量,这意味着:无论我们调用多少次, 中的值都会“记住”其先前的值,即我们在之前的调用中设置:.xlambdarandx(set! x (rand-update x))

因此,您必须尊重 thatlet和 that的位置lambda,否则您创建的过程在调用之间不会有任何“内存”。此外,我不认为该练习要求您创建自己的random程序,只需围绕接受所需消息的内置程序创建一个包装器就足够了:

(define (make-rand)
  (λ (msg)
    (case msg
      ('reset (λ (seed) (random-seed seed)))
      ('generate (random)))))

(define rand (make-rand))

例如:

((rand 'reset) 42)
(rand 'generate)
=> 0.07337258110323383
(rand 'generate)
=> 0.0887121382290788
((rand 'reset) 42)
(rand 'generate)
=> 0.07337258110323383
(rand 'generate)
=> 0.0887121382290788

如果你决定实现你自己的版本random,试着把这些过程分开(就像我上面做的那样),如果你把所有东西都放在一个地方,事情很快就会变得混乱。


推荐阅读