Ленивый поток случайных (ых): когда происходит оценка? - PullRequest
3 голосов
/ 06 апреля 2020

Следующий код, как я думал, должен определять поток случайных чисел в диапазоне от 1 до 10:

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

Однако, что он на самом деле делает, так это определяет поток специфики c случайное число. Например:

> (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)

Так что мне кажется, что константы, понятно, оцениваются при чтении время, тогда как функции оцениваются во время вызова. Обычно это не имеет значения, но в случае потока - когда у вас есть рекурсивное определение - это имеет значение.

Так ли это работает, или это более тонко, чем это ? Есть ли другие случаи, о которых следует знать в отношении этой разницы?

Ответы [ 2 ]

2 голосов
/ 07 апреля 2020

Я согласен с другим ответом, что проблема с кодом 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.

1 голос
/ 06 апреля 2020

Создание 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))

my-stream внутри "остатка" буквально совпадает со значением eq? с одним 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...