我认为,下面的代码应该定义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 1 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)因此,在我看来,常量是在读取时计算的,而函数则是在调用时计算的。通常情况下,这并不重要,但是在流的情况下--你有一个递归的定义--这会产生不同的结果。
这是它的工作方式,还是它比这个更微妙?对于这种差异,是否还有其他的情况值得注意呢?
发布于 2020-04-06 13:55:30
使random-stream成为无参数函数是正确的解决方案。
(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“中的my-stream实际上是与一个my-stream相同的值。
> (eq? my-stream (stream-rest my-stream))
#true因此,因为它们是相同的值,所以可以在函数调用中替换它们。如果(stream-first my-stream)返回5,那么(stream-first (stream-rest my-stream))也必须返回5 (这是因为stream-first是一个“纯”函数,因为它对相同的输入返回相同的输出。)
> (eq? (stream-first my-stream) (stream-first (stream-rest my-stream)))
#true函数版本的情况并非如此,因为每次调用函数时,它都会创建一个新的流值。
(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“字段也调用了(random-stream),所以其余部分与整体不同。
> (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发布于 2020-04-06 22:03:21
我同意另一个答案,即OP代码的问题是,random-stream是一个流,对于这个流,(stream-first random-stream)是一些随机数,而(stream-rest random-stream)也是以相同数目开头的相同流。
不过,我不太同意“没有论证的函数才是正确的解决办法”。
另一种解决方案是使用stream-map将随机数映射到自然数之上:
(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)))此函数可用于创建流(请注意,in-naturals也是创建流的函数):
random_streams.rkt> (define my-stream (random-stream 1 11))
random_streams.rkt> (stream->list (stream-take my-stream 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)))当在用stream-first创建的流上调用random-stream-cons时,返回一个随机数;当在同一流上调用stream-rest时,返回另一个具有随机数的流作为其第一个元素。
所创建的流是持久的:
random_streams.rkt> (stream->list (stream-take random-stream/1-10 10))
'(10 9 9 1 2 7 6 2 6 6)
random_streams.rkt> (stream->list (stream-take random-stream/1-10 15))
'(10 9 9 1 2 7 6 2 6 6 10 1 2 8 5)
random_streams.rkt> (define my-stream-1 (random-stream 1 11))
random_streams.rkt> (stream->list (stream-take my-stream-1 10))
'(1 4 1 10 7 9 9 9 2 9)
random_streams.rkt> (stream->list (stream-take my-stream-1 15))
'(1 4 1 10 7 9 9 9 2 9 2 3 9 9 10)
random_streams.rkt> (define my-stream-2 (random-stream-cons 1 11))
random_streams.rkt> (stream->list (stream-take my-stream-2 10))
'(10 4 6 1 4 2 10 5 3 6)
random_streams.rkt> (stream->list (stream-take my-stream-2 15))
'(10 4 6 1 4 2 10 5 3 6 1 5 7 5 5)这个random-stream-cons/1-10函数与早期的random-stream-cons函数本质上是相同的(但没有参数);但是它们都不是流。它们都是创建流的函数:
(define (random-stream-cons/1-10) (stream-cons (random 1 11) (random-stream-cons/1-10)))每次调用其中一个流创建函数时,都会返回一个新流:
random_streams.rkt> (stream->list (stream-take (random-stream-cons/1-10) 10))
'(10 8 3 10 8 8 1 8 4 5)
random_streams.rkt> (stream->list (stream-take (random-stream-cons/1-10) 10))
'(1 8 7 3 8 2 2 10 6 5)这可能正是我们所希望的;例如,在迭代环境中,这些函数非常有用:
random_streams.rkt> (for ([x (stream-take (random-stream 1 11) 5)])
(displayln x))
2
8
9
1
3因此,返回流的函数是有用的,如果需要,结果流可以绑定到一个符号。对于可能需要多次使用不同值的流,可以在自定义流创建函数中提供参数。但是对于一次性流,stream-map已经完成了返回可以绑定到符号的流的工作,就像OP最初编写的那样。
https://stackoverflow.com/questions/61057542
复制相似问题