程序开始时的当前继续
Current continuation at procedure begin
创建一个 return 过程显然是一个常见的示例,说明可以使用延续创建的内容,如下例所示:
(define (find-multiple factor)
(let/cc return
(for ([num (shuffle (range 2000))])
(when (zero? (modulo num factor))
(return num)))))
(find-multiple 43)
(来自 https://beautifulracket.com/explainer/continuations.html#what-are-they-good-for)
虽然我有点理解,开始的延续是 return 到从某个值调用过程的位置,但我不知道延续实际上是什么样子。在下面的示例中,我可以想象它的样子:
(define c #f)
(+ 1 (+ 2 (+ 3 (+ (let/cc here (set! c here) 4) 5)))) ; 15
(c 20) ; 31
后续是:
(lambda (something)
(+ 1 (+ 2 (+ 3 (+ something 5))))
所以表达式被包装到 lambda 中,let/cc
被替换为 lambda 的输入参数。
但是对于 return 过程,我不知道思考它的正确方法是什么以及延续实际上是什么样子。
首先 let/cc
只是 call/cc
的语法糖。这些是相等的:
(let/cc here
(set! c here) 4)
(call/cc
(lambda (here)
(set! c here) 4))
所有代码,无论您如何编写,都将以特定方式 运行 并且每个操作都将执行一个步骤,然后调用程序其余部分的继续。这个:
(define c #f)
(+ 1 (+ 2 (+ 3 (+ (let/cc here (set! c here) 4) 5)))) ; 15
(c 20) ; 31
变成这样:
((lambda (c k)
(call/cc&
(lambda (here k)
(set! c here)
(k 4))
(lambda (v)
(+& v 5 (lambda (a1)
(+& 3 a1 (lambda (a2)
(+& 2 a2 (lambda (a3)
(+& 1 a3 (lambda (a4)
(c 20 k))))))))))))
#f values)
现在请注意这里的顺序现在是明确的,令人惊讶的是首先处理最深的表达式,因为所有其他表达式都取决于首先计算它的值。另请注意,延续每次都包括对 (c 20)
的调用。
以下是所用程序的 CPS 版本:
(define (+& a b k)
(k (+ a b)))
(define (call/cc& f k)
(f (lambda (v ign-k) (k v)) k))
最后一个可能是您见过的 call/cc
最清晰的实现。虽然您的代码中的那个看起来很神秘,因为代码不是连续传递样式,但在 Scheme 系统使它成为 CPS call/cc
之后甚至不会被视为原始代码。
对于(find-multiple 43)
,延续只是显示结果的 REPL。如果您在 (+ 1 (find-multiple 43))
之类的地方使用过它,那么延续将是 (lambda (v) (+& 1 v halt))
编辑
一个更简单的例子:
(let ((x (read)))
(display
(call/cc
(lambda (return)
(- 4 (if (< x 4) x (return 10))))))))
现在,当您 运行 并输入小于 4 的值时, call/cc
部分未被使用,但如果没有注意到这发生在下一件事的时间它应该做的是从 4
中减去它。在 CPS 中它看起来像这样:
(read&
(lambda (x)
(call/cc&
(lambda (return& k)
(define (k- v)
(-& 4 v k))
(<& x 4 (lambda (p)
(if p
(k- x)
(return& 10 k-)))))
(lambda (v)
(display& v values)))))
这里又是 &-程序。这些可能开始变得熟悉并且有望预测:
(define (<& a b k) (k (< a b)))
(define (-& a b k) (k (- a b)))
(define (display& v k) (k (display v)))
(define (read& k) (k (read)))
创建一个 return 过程显然是一个常见的示例,说明可以使用延续创建的内容,如下例所示:
(define (find-multiple factor)
(let/cc return
(for ([num (shuffle (range 2000))])
(when (zero? (modulo num factor))
(return num)))))
(find-multiple 43)
(来自 https://beautifulracket.com/explainer/continuations.html#what-are-they-good-for)
虽然我有点理解,开始的延续是 return 到从某个值调用过程的位置,但我不知道延续实际上是什么样子。在下面的示例中,我可以想象它的样子:
(define c #f)
(+ 1 (+ 2 (+ 3 (+ (let/cc here (set! c here) 4) 5)))) ; 15
(c 20) ; 31
后续是:
(lambda (something)
(+ 1 (+ 2 (+ 3 (+ something 5))))
所以表达式被包装到 lambda 中,let/cc
被替换为 lambda 的输入参数。
但是对于 return 过程,我不知道思考它的正确方法是什么以及延续实际上是什么样子。
首先 let/cc
只是 call/cc
的语法糖。这些是相等的:
(let/cc here
(set! c here) 4)
(call/cc
(lambda (here)
(set! c here) 4))
所有代码,无论您如何编写,都将以特定方式 运行 并且每个操作都将执行一个步骤,然后调用程序其余部分的继续。这个:
(define c #f)
(+ 1 (+ 2 (+ 3 (+ (let/cc here (set! c here) 4) 5)))) ; 15
(c 20) ; 31
变成这样:
((lambda (c k)
(call/cc&
(lambda (here k)
(set! c here)
(k 4))
(lambda (v)
(+& v 5 (lambda (a1)
(+& 3 a1 (lambda (a2)
(+& 2 a2 (lambda (a3)
(+& 1 a3 (lambda (a4)
(c 20 k))))))))))))
#f values)
现在请注意这里的顺序现在是明确的,令人惊讶的是首先处理最深的表达式,因为所有其他表达式都取决于首先计算它的值。另请注意,延续每次都包括对 (c 20)
的调用。
以下是所用程序的 CPS 版本:
(define (+& a b k)
(k (+ a b)))
(define (call/cc& f k)
(f (lambda (v ign-k) (k v)) k))
最后一个可能是您见过的 call/cc
最清晰的实现。虽然您的代码中的那个看起来很神秘,因为代码不是连续传递样式,但在 Scheme 系统使它成为 CPS call/cc
之后甚至不会被视为原始代码。
对于(find-multiple 43)
,延续只是显示结果的 REPL。如果您在 (+ 1 (find-multiple 43))
之类的地方使用过它,那么延续将是 (lambda (v) (+& 1 v halt))
编辑
一个更简单的例子:
(let ((x (read)))
(display
(call/cc
(lambda (return)
(- 4 (if (< x 4) x (return 10))))))))
现在,当您 运行 并输入小于 4 的值时, call/cc
部分未被使用,但如果没有注意到这发生在下一件事的时间它应该做的是从 4
中减去它。在 CPS 中它看起来像这样:
(read&
(lambda (x)
(call/cc&
(lambda (return& k)
(define (k- v)
(-& 4 v k))
(<& x 4 (lambda (p)
(if p
(k- x)
(return& 10 k-)))))
(lambda (v)
(display& v values)))))
这里又是 &-程序。这些可能开始变得熟悉并且有望预测:
(define (<& a b k) (k (< a b)))
(define (-& a b k) (k (- a b)))
(define (display& v k) (k (display v)))
(define (read& k) (k (read)))