首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SICP 3.6 - Rand程序和局部状态变量

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

Stack Overflow用户
提问于 2020-03-15 11:51:14
回答 2查看 240关注 0票数 1

我在SICP的练习3.6上有困难。它们给出伪随机数生成器的下列代码:

代码语言:javascript
复制
(define rand
  (let ((x random-init))
    (lambda ()
      (set! x (rand-update x))
      x)))

为测试目的,我添加了以下内容:

代码语言:javascript
复制
(define (rand-update x) (+ x 1))
(define random-init 4)

重复应用产生

代码语言:javascript
复制
> (rand)
5
> (rand)
6
> (rand)
7

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

首先,我尝试设置一个具有生成条件的rand。然而,我无意中发现了第一个障碍。

代码语言:javascript
复制
(define (rand-new instruction)
  (let ((x random-init))
    (cond ((eq? instruction 'generate)
          (lambda ()
            (set! x (rand-update x))
            x)))))

给我

代码语言:javascript
复制
> ((rand-new 'generate))
5
> ((rand-new 'generate))
5
> ((rand-new 'generate))
5

将let表达式移到分词中也是如此,如下所示:

代码语言:javascript
复制
(define (rand-new instruction)
  (cond ((eq? instruction 'generate)
         (let ((x random-init))
           (lambda ()
             (set! x (rand-update x))
             x)))))

那么,为什么第一个函数可以工作,而新函数却不能工作呢?是否与条件的使用有关?还是增加了一个参数?

更新(19/03/20)

阅读下一节关于计算环境模型的文章(第3.2节),我得到了理解正在发生的事情所必需的理论。我最后

代码语言:javascript
复制
(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))
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-03-15 13:13:59

理解第一个版本为什么工作(而另一个版本不工作)的关键在于前三行:

代码语言:javascript
复制
(define rand
  (let ((x random-init))
    (lambda ()

正如您所看到的,名称rand被分配给lambda --但是在这样做之前,变量x是在lambda之外的作用域中创建的,这意味着:无论我们调用rand多少次,x中的值都会“记住”它的前一个值,我们在上一次调用中设置了这个值:(set! x (rand-update x))

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

代码语言:javascript
复制
(define (make-rand)
  (λ (msg)
    (case msg
      ('reset (λ (seed) (random-seed seed)))
      ('generate (random)))))

(define rand (make-rand))

例如:

代码语言:javascript
复制
((rand 'reset) 42)
(rand 'generate)
=> 0.07337258110323383
(rand 'generate)
=> 0.0887121382290788
((rand 'reset) 42)
(rand 'generate)
=> 0.07337258110323383
(rand 'generate)
=> 0.0887121382290788

如果您决定实现您自己版本的random,请尝试将这些过程保持独立(正如我前面所做的),如果您将所有内容放在一个地方,事情很快就会变得混乱。

票数 3
EN

Stack Overflow用户

发布于 2020-03-16 08:26:32

x不包含在我们的“随机”过程中。x包含在使我们的“随机”过程中。解决方案的形式如下:

代码语言:javascript
复制
(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可以是:

代码语言:javascript
复制
(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)

我们还可以使用lambdas (匿名过程)实现make-rand

代码语言:javascript
复制
(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)))))))

在这两种情况下,正如奥斯卡所解释的那样,x保持它的价值,因为它不在<proc>/my-rand的范围之内。这一点在第3.2节中作了说明,然后在第4.1节中加以实施。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60692547

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档