把剩余计算作为参数
普通写法中的 addOne(x) 可以直接返回 x + 1;延续写法则让 addOne(x, k) 把 x + 1 交给 k。参数 k 表示拿到这个结果后应该继续的计算,而不只是一个无关的通知函数。
计算 2 × addOne(3) 时,延续可以表达“把返回的数再乘以2”。嵌套计算原先隐藏在调用返回路径里的上下文,因此变成了程序能够命名和传递的内容。
变换暴露控制顺序
完整的 CPS 变换把相关的函数调用改成带延续的形式,并用延续嵌套固定需要的求值顺序。传给下一个函数的延续保存尚待完成的工作;基础算术等原语也可以按所用中间语言的约定保留直接形式。
这可以解释等待网页响应后才继续的回调,也能作为编译器中间形式。程序结构发生变化,却没有因此获得超出原语言可计算能力的新问题求解能力。
尾调用不等于没有保存状态
在完整变换的模型中,控制转移可以采用尾调用,但尚未完成的工作仍须存放在延续中。延续闭包可能保存以后需要的变量,所以不能把尾调用形式直接等同于零分配或零存储。
Shao 与 Appel 的研究把 CPS 中的闭包上下文与调用者、被调用者保存寄存器的选择联系起来。这表明最终成本取决于表示和编译优化;仅仅改写成 CPS 并不保证更快,也不会自动启动新线程。
参考:[3]