Scheme 'call/cc'的函数参数是用CPS编写的吗?

Scheme 'call/cc'的函数参数是用CPS编写的吗?,scheme,lisp,continuations,continuation-passing,callcc,Scheme,Lisp,Continuations,Continuation Passing,Callcc,call/cc的参数是一个过程,它的参数是一个延续。程序是否用CPS编写 没有 CPS样式的函数期望其他普通函数作为它们的参数,并且可以在尾部位置调用它们。这些功能在Scheme方言中被混淆地称为“continuations”。为了消除歧义,我更喜欢“偶然事件” call/cc的argument函数需要一个实际的无限制延续作为其参数。实际的延续不是一个函数。用值调用它会将该值返回到延续的返回上下文中,该返回上下文将与延续一起保存——这是w.r.t.简单函数闻所未闻的壮举 尾部调用函数将其结果返回

call/cc
的参数是一个过程,它的参数是一个延续。程序是否用CPS编写

没有

CPS样式的函数期望其他普通函数作为它们的参数,并且可以在尾部位置调用它们。这些功能在Scheme方言中被混淆地称为“continuations”。为了消除歧义,我更喜欢“偶然事件”

call/cc
的argument函数需要一个实际的无限制延续作为其参数。实际的延续不是一个函数。用值调用它会将该值返回到延续的返回上下文中,该返回上下文将与延续一起保存——这是w.r.t.简单函数闻所未闻的壮举

尾部调用函数将其结果返回到调用函数的调用方上下文中


被调用的continuation将提供的值返回到其creating
call/cc
call的上下文中。因此,它不是一个函数。因此,使用它的函数不是用CPS编写的。

首先,什么是CPS?Continuation-Passing样式是一种将依赖Continuation(即使用
call/cc
运算符)的程序编译成不支持Continuation,但支持词法闭包和尾部调用的目标语言的方法(或者一些尾部调用的传真,比如当堆栈与实际调用帧太深时回滚堆栈的能力)

使用CPS转换的程序本身并不是以延续传递样式编写的。它不一定是这样;它有一个由CPS转换器提供的
call/cc
操作符,可以在需要时访问当前的延续

因为CPS主要是源代码到源代码的转换,所以它是用手写的显式CPS来演示和教学的。通常,Scheme语言用于此。Scheme语言已经有了
call/cc
,但在实验显式的手写CPS时,我们不得不假装
call/cc
不存在r在CPS范例中,我们当然可以提供一个名为
my call/cc
的操作符,它构建在我们的CPS上,与底层方案的
call/cc
无关

在CPS编译语言实现中,每个过程都有一个continuation参数(宿主语言库中的过程除外).call/cc函数的函数参数也不例外。作为CPS世界中的过程,它必须具有continuation参数,以便与传递该参数的过程调用兼容

call/cc
的参数过程实际上可以使用该延续参数,Wikipedia的示例演示了这一点。下面是该示例的副本,其中我将
return
参数重命名为
c
,以减少混淆:

(define (f c)       ;; function used for capturing continuation
  (c 2)             ;; invoke continuation
  3)                ;; if continuation returns, return 3.

(display (f (lambda (x) x))) ; displays 3

(display (call/cc f)) ; displays 2
过程
f
c
参数不必是一个延续;在第一次调用中,它只是一个伪函数
(lambda(x)x)
f
调用它,该函数返回,然后控制权就落到了3

在CPS下,f有一个隐藏的参数,我们可以用手写的CPS来揭示:

(define (f c k)
  (c 2 k)
  (k 3 k))
当返回
3
时,这是因为调用了隐藏的延续。由于程序可以这样做,
f
必须有一个

请注意,
c
k
是不同的延续!可能这就是混淆的地方。
c
延续是调用方当前通过
call/cc
传递的延续,是
call/cc
的显式语义的一部分。
k
是CPS转换器添加的隐藏的延续。它是
f
自身的延续。在经过CPS改造的世界中,每个函数都有一个。在CPS范式下,如果
f
希望返回,它将调用
k
(这就是为什么我将
返回
重命名为
c
)。如果
f
希望继续暂停的
调用/cc
,它将调用
c

顺便说一句,在自动CPS下,
k
不会被字面上称为
k
,因为这不卫生:程序可以自由绑定
k
符号。它必须是机器生成的符号(gensym),或者接受其他形式的卫生处理

唯一需要在CPS下特别处理的函数是主机语言/VM中的库函数,该库函数可用于CPS翻译语言。当CPS翻译语言调用
(display obj)时
要打印一个对象,
display
函数必须是一个重命名的包装器,可以接受continuation参数(然后忽略它并调用真正的
display
函数,而不使用它),否则对
display
的调用必须由CPS转换器专门处理,以忽略continuation参数

最后,为什么CPS可以用一种本机不提供连续性的语言/虚拟机实现连续性?原因是CPS转换程序中的所有函数调用都是尾部调用,因此永远不会返回。实现连续性的难点在于捕获整个调用堆栈,以便在继续时可以返回。这样的fea真只能在语言实现级别添加。在20世纪70年代,InterLisp使用了“意大利面堆栈”要实现这一点:堆栈帧是垃圾收集的堆对象,指向父帧。但是如果函数不执行返回之类的操作怎么办?那么就不需要在实现中添加意大利面堆栈了。请注意,意大利面堆栈并没有完全消失:我们在CPS下有一些等效的东西,即捕获的数据链词法环境。延续是lambda,它捕获周围的