Схема: проблема с отображением при использовании выражения `delay` - PullRequest
2 голосов
/ 01 марта 2020

Это проблема, связанная с ex3.51 в SICP, вот код

(define (cons-stream x y)
  (cons x (delay y)))
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
(define (stream-map proc s)
  (if (stream-null? s)
      the-empty-stream
      (cons-stream
       (proc (stream-car s))
       (stream-map proc (stream-cdr s)))))
(define (stream-enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons-stream
       low
       (stream-enumerate-interval (+ low 1) high))))
(define (stream-ref s n)
  (if (= n 0)
      (stream-car s)
      (stream-ref (stream-cdr s) (- n 1))))
(define (show x)
  (display x)
  x)
;test
(stream-map show (stream-enumerate-interval 0 10))

, вывод 012345678910(0 . #<promise>).

, но я думал, что выражение задержки в cons-stream отложил оценку, если я использую другую функцию обработки в stream-map как lambda (x) (+ x 1) вывод (1 . #<promise>) более разумный, так почему display печатает все числа?

1 Ответ

2 голосов
/ 01 марта 2020

Проблема с этим определением:

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

...