Random 惰性随机流:何时进行评估?

Random 惰性随机流:何时进行评估?,random,stream,scheme,racket,lazy-evaluation,Random,Stream,Scheme,Racket,Lazy Evaluation,我认为,下面的代码应该定义1到10之间的随机数流: (define random-stream (stream-cons (random 1 11) random-stream)) 然而,它实际上是定义一个特定随机数的流。例如: > (stream->list (stream-take random-stream 10)) '(5 5 5 5 5 5 5 5 5 5) 我假设这是第一次解析定义时(random 11)产生的随机数。我通过使random stream成为一个无参数函

我认为,下面的代码应该定义1到10之间的随机数流:

(define random-stream (stream-cons (random 1 11) random-stream))
然而,它实际上是定义一个特定随机数的流。例如:

> (stream->list (stream-take random-stream 10))
'(5 5 5 5 5 5 5 5 5 5)
我假设这是第一次解析定义时
(random 11)
产生的随机数。我通过使
random stream
成为一个无参数函数来解决这个问题:

(define (random-stream) (stream-cons (random 1 11) (random-stream)))
这项工作:

> (stream->list (stream-take (random-stream) 10))
'(6 1 10 9 4 2 2 3 3 10)
所以在我看来,可以理解的是,常数是在读取时计算的,而函数是在调用时计算的。通常这并不重要,但在流的情况下——您有一个递归定义——这会有所不同


它是这样工作的,还是比这更微妙?关于这种差异,还有其他情况需要注意吗?

使
随机流
成为无参数函数是正确的解决方案

(define (random-stream) (stream-cons (random 1 11) (random-stream)))
我会解释原因

当您通常使用
(define my stream(stream cons…))
定义流时,该流只有一个值。对
my stream
的任何引用都将产生相同的值

(define my-stream (stream-cons (random 1 11) my-stream))
“rest”中的
mystream
mystream
中的
eq?
值完全相同

> (eq? my-stream (stream-rest my-stream))
#true
因为它们是相同的值,所以可以在函数调用中替换它们。如果
(stream first my stream)
返回5,则
(stream first(stream rest my stream))
也必须返回5。(这是因为
stream first
是一个“纯”函数,它为相同的输入返回相同的输出。)

函数版本并非如此,因为每次调用函数时,它都会创建一个新的流值

(define (random-stream) (stream-cons (random 1 11) (random-stream)))

> (eq? (random-stream) (random-stream))
#false
> (eq? (stream-first (random-stream)) (stream-first (random-stream)))
#false
由于“rest”字段也调用
(随机流)
,因此rest与整个字段不同

> (define generated-stream (random-stream))
> (eq? generated-stream (stream-rest generated-stream))
#false
> (eq? (stream-first generated-stream) (stream-first (stream-rest generated-stream)))
#false

我同意另一个答案,即OP code的问题是
随机流
是一个流,其中
(流优先随机流)
是一个随机数,而
(流剩余随机流)
也是以相同数开头的相同流

不过,我并不完全同意“无参数函数是正确的解决方案”

另一种解决方案是使用
流映射
映射自然数上的随机数:

(define random-stream/1-10
  (stream-map (lambda (x) (random 1 11)) (in-naturals)))
创建一个生成随机数流的函数会更好:

(define (random-stream a b)
  (stream-map (lambda (x) (random a b)) (in-naturals)))
此函数可用于创建流(请注意,naturals中的
也是创建流的函数):

random_streams.rkt>(定义我的流(random stream 11))
random_streams.rkt>(流->列表(流取我的流10))
'(1 1 2 7 5 7 4 2 2 9)
使用创建流的函数的这种思想,可以拯救
stream cons
方法:

(define (random-stream-cons a b)
  (stream-cons (random a b) (random-stream-cons a b)))
当对使用
随机流cons
创建的流调用
stream first
时,将返回一个随机数;当对同一个流调用
stream rest
时,将返回另一个以随机数作为其第一个元素的流

创建的流是持久的:

random_streams.rkt>(流->列表(流取随机流/1-10))
'(10 9 9 1 2 7 6 2 6 6)
随机流.rkt>(流->列表(流取随机流/1-10 15))
'(10 9 9 1 2 7 6 2 6 6 10 1 2 8 5)
random_streams.rkt>(定义my-stream-1(random stream 1 11))
random_streams.rkt>(流->列表(流取my-stream-1 10))
'(1 4 1 10 7 9 9 9 2 9)
random_streams.rkt>(流->列表(流取my-stream-1 15))
'(1 4 1 10 7 9 9 9 2 9 2 3 9 9 10)
random_streams.rkt>(定义my-stream-2(random stream cons 1 11))
random_streams.rkt>(流->列表(流取my-stream-2 10))
'(10 4 6 1 4 2 10 5 3 6)
random_streams.rkt>(流->列表(流取my-stream-2 15))
'(10 4 6 1 4 2 10 5 3 6 1 5 7 5 5)
随机流cons/1-10
函数与先前的
随机流cons
函数基本相同(但没有参数);然而,它们都不是溪流。它们都是创建流的函数:

每次调用其中一个流创建函数时,都会返回一个新流:

random_streams.rkt>(流->列表(流获取(random stream cons/1-10)10))
'(10 8 3 10 8 8 1 8 4 5)
随机流.rkt>(流->列表(流获取(随机流cons/1-10)10))
'(1 8 7 3 8 2 2 10 6 5)
这可能正是人们所期望的;此类函数非常有用,例如,在迭代上下文中:

random\u streams.rkt>(用于([x(流获取(随机流11)5)])
(显示Ln x)
2.
8.
9
1.
3.

因此,返回流的函数很有用,如果需要,可以将结果流绑定到符号。对于可能需要多次使用不同值的流,可以在自定义流创建函数中提供参数。但对于一次性流,
stream map
已经完成了返回流的工作,该流可以绑定到OP最初编写的符号。

Racket太认真了,但更严重的是,我认为这是由于流中的记忆。请看。虽然
(eq?my stream(stream rest my stream))
是真的,但我认为它并不一定意味着“如果……返回5,那么……也必须返回5”
random
random
彼此是
eq?
,但是
(random)
(random)
不需要计算到相同的值。好吧,你是对的,有一个隐藏的假设,
流优先
是“纯的”函数的含义是,它为相同的输入返回相同的输出。我必须指定我所说的“纯”是什么意思,因为它在没有副作用的意义上不是“纯”,而是在相同的输入相同的输出意义上是“纯”。也许
(随机流)
更好地命名为
(生成随机流)
…我不明白
(define (random-stream-cons a b)
  (stream-cons (random a b) (random-stream-cons a b)))
(define (random-stream-cons/1-10) (stream-cons (random 1 11) (random-stream-cons/1-10)))