Continuation-Passing Style(c#)

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,就是++xConsole.WriteLine(x)

Continuation封装为函数的一个参数,当函数执行完时,不是返回,而是通过调用传入的Continuation传递计算结果,继续往下执行,这种方式就是CPS。

CPS改造

比如取最大值函数

 static int Max(int n, int m)
{
    if (n > m)
        return n;
    else
        return m;
}

转为CPS需要这几步:

  1. 修改返回值类型为void
  2. 添加一个额外的Continuation参数Action<T>, 其中T是函数原本的返回类型。
  3. 用新添加的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写的代码不如普通的控制流容易理解,一般可能编译器的书写者比较熟悉他的含义。

posted @ 2020-05-03 10:44  mosakashaka  阅读(262)  评论(0编辑  收藏  举报