繁体   English   中英

SICP 3.6 - Rand 过程和局部状态变量

[英]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 ,这意味着:无论我们调用多少次randx的值都会“记住" 它之前的值,我们在之前的调用中(set! x (rand-update x))(set! x (rand-update x))

因此,您必须尊重letlambda ,否则您创建的过程在调用之间将没有任何“内存”。 此外,我认为该练习不是要求您创建自己的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> ,它:

  1. 给定消息'generate返回下一个随机数。
  2. 给定消息'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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM