[英]SICP 3.6 - Rand procedure and local state variables
我在 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
以便它接受一个参数,指示它要么'generate
要么'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))
理解为什么第一个版本有效(以及为什么另一个无效)的关键在于前三行:
(define rand
(let ((x random-init))
(lambda ()
如您所见,名称rand
被分配给一个lambda
- 但在此之前,在lambda
之外的范围内创建了一个变量x
,这意味着:无论我们调用多少次rand
, x
的值都会“记住" 它之前的值,我们在之前的调用中(set! x (rand-update x))
: (set! x (rand-update x))
。
因此,您必须尊重let
和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
版本,请尝试将过程分开(就像我上面所做的那样),如果您将所有内容放在一个地方,事情很快就会变得混乱。
x
不包含在我们的“随机”程序中。 x
包含在使我们的“随机”过程的过程中。 溶液的形状是:
(define (make-rand)
(define x 0)
...
<proc>)
(define my-rand (make-rand))
((my-rand 'reset) 42)
(my-rand 'generate)
(my-rand 'generate)
所以make-rand
返回一个过程<proc>
,它:
'generate
返回下一个随机数。'reset
返回另一个过程,该过程采用新值并将其分配给 x。 使用定义(命名过程) make-rand
可以是:
(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) ; 'dispatch' returned and assigned to my-rand
'reset
消息返回一个过程,例如, (my-rand 'reset)
返回set-x!
所以((my-rand 'reset) 42)
相当于(set-x! 42)
。
我们还make-rand
使用 lambdas(匿名程序)实现make-rand
:
(define (make-rand)
(let ((x 0))
(lambda (message) ; lambda returned and assigned to my-rand
(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)))))))
在任何一种情况下,正如 Oscar 解释的那样, x
保持它的价值,因为它超出了<proc>
/ my-rand
。 这在 §3.2 中描述,然后在 §4.1 中实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.