Scheme 中的 call/cc 与 Python 和 JavaScript 中的 yield 是一样的吗?
Does call/cc in Scheme the same thing with yield in Python and JavaScript?
Scheme 中的 call/cc
与 Python 和 JavaScript 中的 yield
是一回事吗?
我对发电机不是很清楚。在我看来,yield
使语言能够轻松生成迭代器。但是我不确定我说的对不对
Scheme中的call/cc
是否与其他语言中的yield
相关?如果有,它们是一样的,还是有什么区别?
谢谢!
下面是定义生成器的代码:
(define-syntax define-generator
(lambda (x)
(syntax-case x (lambda)
((stx name (lambda formals e0 e1 ...))
(with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
(syntax (define name
(lambda formals
(let ((resume #f) (return #f))
(define yield
(lambda args
(call-with-current-continuation
(lambda (cont)
(set! resume cont)
(apply return args)))))
(lambda ()
(call-with-current-continuation
(lambda (cont)
(set! return cont)
(cond (resume (resume))
(else (let () e0 e1 ...)
(error 'name "unexpected return"))))))))))))
((stx (name . formals) e0 e1 ...)
(syntax (stx name (lambda formals e0 e1 ...)))))))
my blog 中有使用生成器的示例。生成器以类似于 Python 中的 yield
的方式使用 call-with-current-continuation
,但更通用。
您可以使用 call/cc 实现生成器。这是一个例子:
它们的工作方式与 python 和 ECMAScript 生成器类似。
call/cc
是一种比生成器更通用的语言特性。因此,您可以使用 call/cc
创建生成器,但不能使用生成器创建 call/cc
。
如果你有一个计算值并在其他地方使用这些值的程序,它基本上是一个步进机器。人们可能会认为它是一个程序,每个步骤都有一个函数,其余部分有一个延续脚步。因此:
(+ (* 3 4) (* 5 6))
可以解释为:
((lambda (k)
(k* 3 4 (lambda (v34)
(k* 5 6 (lambda (v56)
(k+ v34 v56 k)))))
halt)
k 前缀仅表示它是基元的 CPS 版本。因此,他们将最后一个参数称为带有结果的函数。另请注意,在 Scheme 中未定义的计算顺序实际上是在此重写中选择的。在这种美丽的语言中 call/cc
就是这样:
(define (kcall/cc kfn k)
(kfn (lambda (value ignored-continuation)
(k value))
k))
所以当你这样做时:
(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6)))))
; ==> 18
在幕后发生了这种情况:
((lambda (k)
(k* 3 4 (lambda (v34)
(kcall/cc (lambda (exit k)
(exit 6 (lambda (v6)
(k* 5 v6 k)))
k))))
halt)
通过使用替换,我们可以证明这实际上完全符合预期。由于调用了退出函数,因此从不调用原始延续,因此取消了计算。与 call/cc
为我们提供这种看似不明显的延续相比,它在 CPS 中并不神奇。因此,call/cc
的大部分魔力都在编译阶段。
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
有人可能 ,但这只是最上面的糖。
更多示例 I love Matt Mights page 有很多关于如何使用延续的示例。
Scheme 中的 call/cc
与 Python 和 JavaScript 中的 yield
是一回事吗?
我对发电机不是很清楚。在我看来,yield
使语言能够轻松生成迭代器。但是我不确定我说的对不对
Scheme中的call/cc
是否与其他语言中的yield
相关?如果有,它们是一样的,还是有什么区别?
谢谢!
下面是定义生成器的代码:
(define-syntax define-generator
(lambda (x)
(syntax-case x (lambda)
((stx name (lambda formals e0 e1 ...))
(with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
(syntax (define name
(lambda formals
(let ((resume #f) (return #f))
(define yield
(lambda args
(call-with-current-continuation
(lambda (cont)
(set! resume cont)
(apply return args)))))
(lambda ()
(call-with-current-continuation
(lambda (cont)
(set! return cont)
(cond (resume (resume))
(else (let () e0 e1 ...)
(error 'name "unexpected return"))))))))))))
((stx (name . formals) e0 e1 ...)
(syntax (stx name (lambda formals e0 e1 ...)))))))
my blog 中有使用生成器的示例。生成器以类似于 Python 中的 yield
的方式使用 call-with-current-continuation
,但更通用。
您可以使用 call/cc 实现生成器。这是一个例子:
它们的工作方式与 python 和 ECMAScript 生成器类似。
call/cc
是一种比生成器更通用的语言特性。因此,您可以使用 call/cc
创建生成器,但不能使用生成器创建 call/cc
。
如果你有一个计算值并在其他地方使用这些值的程序,它基本上是一个步进机器。人们可能会认为它是一个程序,每个步骤都有一个函数,其余部分有一个延续脚步。因此:
(+ (* 3 4) (* 5 6))
可以解释为:
((lambda (k)
(k* 3 4 (lambda (v34)
(k* 5 6 (lambda (v56)
(k+ v34 v56 k)))))
halt)
k 前缀仅表示它是基元的 CPS 版本。因此,他们将最后一个参数称为带有结果的函数。另请注意,在 Scheme 中未定义的计算顺序实际上是在此重写中选择的。在这种美丽的语言中 call/cc
就是这样:
(define (kcall/cc kfn k)
(kfn (lambda (value ignored-continuation)
(k value))
k))
所以当你这样做时:
(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6)))))
; ==> 18
在幕后发生了这种情况:
((lambda (k)
(k* 3 4 (lambda (v34)
(kcall/cc (lambda (exit k)
(exit 6 (lambda (v6)
(k* 5 v6 k)))
k))))
halt)
通过使用替换,我们可以证明这实际上完全符合预期。由于调用了退出函数,因此从不调用原始延续,因此取消了计算。与 call/cc
为我们提供这种看似不明显的延续相比,它在 CPS 中并不神奇。因此,call/cc
的大部分魔力都在编译阶段。
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
有人可能
更多示例 I love Matt Mights page 有很多关于如何使用延续的示例。