Проблема с этим определением:
(define (cons-stream x y)
(cons x (delay y)))
Он определяет cons-stream
как функцию, поскольку он использует define
.
Оценка схемы рвется : аргументы оцениваются перед вводом тела функции. Таким образом, y
уже полностью вычислен, когда он передается в delay
.
Вместо этого cons-stream
должен быть определен как макрос, например
(define-syntax cons-stream
(syntax-rules ()
((_ a b) (cons a (delay b)))))
или мы можем вызвать delay
явно, вручную, например,
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons
(proc (stream-car s))
(delay
(stream-map proc (stream-cdr s))))))
Тогда в нашем коде не будет вызовов на cons-stream
, только вызовы (cons A (delay B))
. И delay
является макросом (или специальной формой, что угодно), он не оценивает свои аргументы перед работой, а вместо этого идет прямо к манипулированию выражениями аргументов.
И мы могли бы даже сбросьте вызовы на delay
и замените (cons A (delay B))
на (cons A (lambda () B))
. Это также повлечет за собой переопределение force
(которое является встроенным и идет вместе со встроенным delay
) как просто (define (force x) (x))
или просто вызов (x)
вручную, где это уместно, чтобы вызвать хвост потока.
Вы можете увидеть такой код потоков на основе lambda
ближе к концу этого ответа или запись идеона (для этой записи RosettaCode ) без каких-либо макросов, используя вместо этого явные лямбды. Этот подход может изменить производительность кода, поскольку delay
запоминает, а потоки на lambda
- нет. Разница будет видна, если мы когда-нибудь попытаемся получить доступ к элементу потока более одного раза.
См. Также этот ответ для еще одного подхода к реализации потоков, хирургически изменяющего последнюю cons-ячейку списка как запоминание force
.