简短的回答:当然有道理,你可以直接应用CPS变换,你只会有很多错误,因为你注意到每个参数都会有自己的附加延续
在你的例子中,我会考虑有一个 +(x,y) 原始的,原始的,你问的是什么是翻译
+(x,y)
let add x y = +(x,y)
(这个 add 忠实地代表OCaml的 (+) 运营商)
add
(+)
add 在语法上等同于
let add = fun x -> (fun y -> +(x, y))
因此,您应用CPS转换¹并获取
let add_cps = fun x kx -> kx (fun y ky -> ky +(x,y))
如果你想要一个看起来更像是你愿意写的东西的翻译代码,你可以设计一个更精细的转换,实际上将已知的arity函数视为非curried函数,并将所有参数作为一个整体进行处理(就像你在非咖喱语言,以及功能编译器已经出于明显的性能原因而已。
¹:我写道“ 一个 CPS转换“因为没有”一个真正的CPS翻译“。已经设计了不同的翻译,产生或多或少与延续相关的垃圾。正式的CPS翻译通常直接在lambda演算上定义,所以我想你有一个不太正式,更加手工制作的CPS转变。
CPS的良好特性(作为 样式 程序方面,而不是对这种风格的特定转换)是评估的顺序是完全明确的,并且所有调用都是尾调用。只要你尊重这些,你就可以做得相对自由。因此,专门处理curryfied功能是完全正确的。
备注:你的 (cps-add k 1 2) 如果你假设编译器检测并优化cps-add实际上总是需要3个参数,并且不构建中间闭包,那么也可以认为version是尾递归的。这可能看起来很牵强,但在使用这些语言推理非CPS程序中的尾调用时,我们使用的假设完全相同。
(cps-add k 1 2)
是的,从技术上讲,所有函数都可以用一种方法分解为函数,但是,当你想使用CPS时,你唯一要做的就是说在某个计算点运行continuation方法。
使用您的示例,让我们来看看。为了使事情变得更容易一些,让我们解析cps-add到它的正常形式,它只是一个参数的函数。
(cps-add k) - > n - > m = k((+)n m)
请注意,此时继续k未被评估(这可能是您的混淆点吗?)。
这里我们有一个名为cps-add k的方法,它接收一个函数作为参数,然后返回一个带有另一个参数n的函数。
((cps-add k)n) - > m = k((+)n m)
现在我们有一个带参数的函数,m。
所以我想我想要指出的是,currying不会妨碍CPS风格的编程。希望在某种程度上有所帮助。