延续传递样式转换规则是什么?



我正在尝试理解延续传递样式转换。

我试图建立一个方案到C编译器,我想使用延续传递风格。不管延续传递样式是不是正确的方法你们能解释一下转换规则吗?

例如,以下是Gambit Scheme的创造者Marc Feeley的演讲。

我将总结他给出的延续传递样式规则,但注意:我不理解它们。特别是我不明白C是什么。

符号如下:

[E]
C

,表示延续c中表达式E的延续传递样式转换。

例如,一个转换规则是这样的:
[c] 
C
==>
(C c) ;; application of C to c

,表示连续c中常数c的CPS转换。

C是什么?我很难理解C是什么。这就像魔法一样。

[(if E1 E2 E3)]
C
==>
[E1]
(lambda (r1)
(if r1 [E2] [E3]))
C    C

E1传递给r1

但是C是什么?

你们能解释一下吗?

谢谢。

如果您向上滚动到第7页,您将发现延续是什么,这对于理解转换为延续传递样式的规则是必要的。一个例子是

> (sqrt (+ (read) 1))

,它注意到(read)的延拓是

取一个值,加1,计算它的平方根,打印结果并进入下一个REPL交互。

因此表达式E的延续C是"无论该表达式的值发生什么变化"。这一点在第20页得到了重申,示例

(let ((square (lambda (x) (* x x))))
(write (+ (square 10) 1)))

,其中(square 10)的延拓为

(lambda (r) (write (+ r 1)))

因此,当您递归地将程序转换为CPS风格时,随着您深入到表达式,延续C将增长。注意,每个翻译规则[E]|C的结果是一个较小的未翻译的E,如果E足够简单,可能是空的,和一个较大的翻译的c。

当您在CPS中转换代码时,您实际上在求值中引入了严格的规程。

当你写(+ x y z)时,你计算+,x, y, z的顺序是不清楚的。如果你写的语言明确地定义了一个顺序,你知道会发生什么。但是,如果语言没有插入顺序,您可以通过将代码写入/转换为CPS来定义您希望的顺序,在我建议的示例中,您将写入:

(eval + (lambda (+)
(eval x (lambda(x)
(eval y (lambda (y)
(eval z (lambda (z)
(+ x y z))))

如果你想求左右值。

如果你用CPS写代码,这就像用汇编语言写代码一样,因为每段代码都可以与一条指令相关联,而这条指令在非常低的编程中具有相应的指令。如果在CPS中转换某些代码,则需要唯一地重命名变量以避免冲突。在创建C语言的时候,我认为它没有明确定义CPS转换;这就是inline说明符拒绝递归调用的原因。通过重写C代码并使用CPS转换,可以将递归函数转换为goto-loop,但标准C不希望这样做。

将代码转换为CPS的方法有很多。例如,在Mit-scheme中,输入代码没有显式地以CPS形式重写,但评估过程使用goto语句和trampoline调用的组合来模拟它(这是一种在学校里学不到的方法,但在实践中使用)。

递归CPS代码可以直接转换为迭代循环(这就是为什么scheme->C翻译器首先进行转换)来解决尾部递归。Dan Friedman的EPL第一版详细介绍了这一点。弗里德曼也有一篇关于这方面的文章。如果你找不到,我会试着帮你找。

最新更新