我把未来抛给了你。

有话好好说别乱来啊

两个数相加,翻倍,输出结果。可以这么写

1
2
3
4
5
function OutputDoubleSum(a,b){
var result = a + b;
result = result * 2;
console.log(result);
}

也可以这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Sum(a,b){
return a+b;
}

function Double(x){
return x*2;
}

function Output(x){
console.log(x);
}

function OutputDoubleSum(a,b){
Output(Double(Sum(a,b)));
}

还能这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Sum(a,b,k){
k(a+b);
}

function Double(x,k){
k(x*2);
}

function Output(x,k){
k(console.log(x));
}

function OutputDoubleSum(a,b){
Sum(a,b,function (sumxy){
Double(sumxy,function (doublex){
Output(doublex,function(){})
})
})
}

第一种是顺序风格,第二种是函数组合风格,第三种是后继传递风格。

后继传递风格Continuation-passing style)是一种控制流通过参数传递的风格。

简单的说就是把后继,也就是下一步要运行的代码,封装成函数,通过参数传递的方式传给当前运行的函数。

在例子中 Output(x,k) 的第二个参数接受一个后继,在 Output 执行完成后调用后继。 Output 的后继为

1
function(){}

是一个空函数,表示后面什么事情都不做。

Double 的后继是

1
2
3
function (doublex){
Output(doublex,function(){})
})

表示收到 Double 的计算结果后,执行 Output

Sum 的后继是

1
2
3
4
5
function (sumxy){
Double(sumxy,function (doublex){
Output(doublex,function(){})
})
})

表示收到 Sum 的计算结果后,执行 Double

这样通过参数传递的方式将顺序风格改为了后继传递风格。

所以有什么用呢

没什么用。当然是有用的啦,不然干嘛这么费力不讨好。

一个最常见的用法是用在异步代码中,也就是异步代码的回调

搭配 Call/cc 能够实现控制流中的 try/catchyield 等控制方式。

最后

「CPS 就是把用于经典逻辑和直觉逻辑间命题转换的 Gödel–Gentzen 转换,经 Curry–Howard correspondence 应用到证明过程表示的自然结果。」(逃