Я пытаюсь перенести yield
и yield from
с Python на Scheme.
Вот реализация, которую я сделал:
(define (coroutine routine)
(let ((current routine)
(status 'new))
(lambda* (#:optional value)
(let ((continuation-and-value
(call/cc (lambda (return)
(let ((returner
(lambda (value)
(call/cc (lambda (next)
(return (cons next value)))))))
(if (equal? status 'new)
(begin
(set! status 'running)
(current returner))
(current (cons value returner)))
(set! status 'dead))))))
(if (pair? continuation-and-value)
(begin (set! current (car continuation-and-value))
(cdr continuation-and-value))
continuation-and-value)))))
Проблема с этой реализацией заключается в том, что способ ее вызова не похож на Python yield
.
(define why (call/cc (lambda (yield)
(format #t "love me or leave me!")
(yield "I leave!")
;; the program never reach this part
(format #t "it probably left :("))))
(format #t "return actually populates WHY variable\n")
(format #t "WHY: ~a\n")
Среди прочего, каждый раз, когда мне нужно перезапустить сопрограмму, я должен let
создать новую return
переменную, чтобы иметь возможность exit
сопрограммы. В принципе, я нахожу синтаксис слишком многословным. Есть ли другой, чтобы иметь более чистый синтаксис?
Должна быть возможность yield
и send
значений для сопрограммы. Вот пример того, как должна использоваться сопрограмма:
(define-coroutine (zrange start step)
"compute a range of values starting a START with STEP between
each value. The coroutine must be restarted with 0 or more, which
is added to the step"
(let loop ((n start))
(loop (+ n step (yield n)))))
(coroutine-map (zrange 0 10) '(1 100 1000 10000 100000))
;; => 0 110 1120 11130 111140
В приведенном выше примере 1
игнорируется, а затем 100
, 1000
передаются send
генератору. Я сделал реализацию на основе кода @sylwester, но у меня проблемы с макросом:
(define (make-generator procedure)
(define last-return #f)
(define last-value #f)
(define last-continuation (lambda (_) (procedure yield)))
(define (return value)
(newline)(display "fuuu")(newline)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda* (. rest) ; ignore arguments
(call/cc (lambda (yield)
(set! last-return yield)
(apply last-continuation rest)))))
(define-syntax define-coroutine
(syntax-rules ()
((_ (name args ...) body ...)
(define (name args ...)
(make-generator
(lambda (yield)
body ...))))))
(define-coroutine (zrange start step)
(let loop ((n start))
(loop (+ n step (yield n)))))
(display (map (zrange 0 10) '(1 100 1000 10000 100000)))
coroutine-map
? где вzrange
вы берете аргумент? - person Sylwester   schedule 08.06.2015yield
не является аргументом zrange. Я думаю, что это требует негигиеничных макросов. - person amirouche   schedule 08.06.2015coroutine-map
перебирает значения, возвращаемые (zrange 0 10), пока не возникнет ошибка. - person amirouche   schedule 08.06.2015coroutine-map
узнает, что он должен+
соединить элементы вместе? Что делать, если вы хотите умножить? с аргументами, которые я имею в видуsend
, можете ли вы отправить больше значений вzrange
, если он имеет конечную длину? Будет ли это какyielding
каждый по порядку внизу? - person Sylwester   schedule 08.06.2015send
что-то делаете, генератор перезапускается иyield
возвращает отправленное значение. Вот почему(+ n step (yield n))
становится(+ 0 10 100)
. Я просто понял, что первое значение карты не учитывается в моей реализации. Я добавлю реализацию, которую я сделал. - person amirouche   schedule 08.06.2015yield
, представленный в вашем макросе, отличается от того, что находится в теле. Вместо( . rest)
вы просто хотитеrest
. Я думаю, что знаю, чего вы хотите, поэтому я обновлю свой ответ. - person Sylwester   schedule 08.06.2015