Continuation-Passing Style
(简写为CPS)是一种代码控制流的实现方式。内容部分来自这里和这里。
Continuation
Continuation
是指在一个逻辑算法或者功能中,某一计算时刻,之后的所有计算逻辑。
例如
static void M()
{
var x = 5;
F(); // <----------- Point in the computation
++x;
Console.WriteLine(x);
}
static void Main()
{
M();
Console.WriteLine("Done");
}
那么在调用F
时的Continuation,就是++x
和Console.WriteLine(x)
。
将Continuation
封装为函数的一个参数,当函数执行完时,不是返回,而是通过调用传入的Continuation
传递计算结果,继续往下执行,这种方式就是CPS。
CPS改造
比如取最大值函数
static int Max(int n, int m)
{
if (n > m)
return n;
else
return m;
}
转为CPS需要这几步:
- 修改返回值类型为
void
- 添加一个额外的Continuation参数
Action<T>
, 其中T是函数原本的返回类型。 - 用新添加的Continuation参数替换原函数中所有的
return
语句,传入的参数是原返回值
static void Max(int n, int m, Action<int> k)
{
if (n > m)
k(n);
else
k(m);
}
于是原本调用方式也从:
static void Main()
{
Console.WriteLine(Max(3, 4));
}
变为:
static void Main()
{
Max(3, 4, x => Console.WriteLine(x));
}
用CPS实现一个控制流程。
?
运算符基本是所有语言都有的一个三目运算符。不过假如没有呢?那么可以考虑利用CPS实现一个库函数达到问好运算符的目的。
M(B() ? C() : D())
如上函数,我们可以定义一个Conditional
函数实现这个逻辑。
T Conditional<T>(bool b, Func<T> consequence, Func<T> alternative)
{
if (b) return consequence(); else return alternative();
}
//于是可以使用:
M(Conditional(B(), ()=>C(), ()=>D()))
其他
CPS可以是函数更灵活的控制执行流程,实现不同的控制流。c#中的异步(async/await
)就使用了CPS实现(以及状态机)。
除此之外,CPS可以可以节省栈内存,因为函数的执行不需要记录返回值,返回值直接通过传入的Continuation
进行下一步处理了。
但是CPS写的代码不如普通的控制流容易理解,一般可能编译器的书写者比较熟悉他的含义。