这篇文章是在阅读Eric Lippert大神的MSDN Blog文章时同步写成的,其中主要是各种翻译,同时还混杂自己阅读文章的笔记和感想。

原博文地址 http://blogs.msdn.com/b/ericlippert/archive/2010/10/21/continuation-passing-style-revisited-part-one.aspx

CPS是Continuation Passing Style的缩写,关于这种风格,E.L.大神和通常的程序风格做了一个对比:

通常的程序:

  记录下程序当前的状态,比如本地变量的值等,一般保存在栈里面

  把程序的控制权交给“子过程”,直到其返回

  恢复到调用前的状态,此时已经知道了子过程的返回值

而CPS是一种没有“子过程”和“返回”的风格。取而代之的是,当前函数执行的最后一条指令是调用下一个函数,因为没有函数会返回,你也就不需要保存调用之前的值,现在程序在什么地方也没有什么意义,因为程序永远不会回来。

显然从这里看,结合以前的知识,CPS变换也就是所谓的尾递归形式,因为是尾调用,所以前面栈上的东西也就没有存在意义了,于是优化的时候栈空间可以重复利用,也就是所谓的尾递归优化。

为了确保事情按我们想要的顺序发生,调用新的函数的时候给他传一个continuation,continuation本身也是一个函数,它负责执行后面所有的东西。

E.L.大神用C#写的例子,幸好我看得懂:(毕竟人家是C#的爹们之一)

我们有以下四个函数 string M(int); bool B(); int C(); int D();

假定C#没有?:三目运算符,那么该如何实现 M(B() ? C() : D());

忽略废话,我们有

T Conditional<T>(bool b, Func<T> consequence, Func<T> alternative)
{
if (b) return consequence(); else return alternative();
}

直接调用 M(Conditional(B(), ()=>C(), ()=>D()); 就好

(C#的lambda写起来真是爽)

好了下面要强行把Conditional写成CPS风格的,怎么办,这么分析:

  M调用结束后应该调用一些东西(用来继续控制流),姑且叫它currentContinuation,先不管它到底是什么样

  需要重写B使得B接受一个接受bool的continuation,姑且用Action<bool>代替“一个接受bool的continuation”,现在有了void B(Action<bool>)

  B的continuation,也就是在B之后发生的事情是什么?

    先跳步:形式是 B(b=>M(Conditional(b, ()=>C(), ()=>D()))) ,B接受的lambda接受一个bool,然后把它传给Conditional

  现在B是CPS了,但是传给B的lambda不是CPS的,为了让lambda也变成CPS的,我们重复对B的分析:

    M需要接受一个Action<string>,C和D需要接受Action<int>

    至于Conditional,仍然需要对他的参数有惰性求值的特性,但是调用的那些lambda已经没有返回值了,所以,那个lambda也要接受一个continuation。

  所以有: B(b=>Conditional(b, c=>C(c), c=>D(c), t=>M(t, currentContinuation)))

Conditional现在是这样的:

void Conditional<T> (bool condition, Action<Action<T>> consequence, Action<Action<T>> alternative, Action<T> continuation)
{
if (condition)
consequence(continuation) ;
else
alternative(continuation);
}

总之:B执行然后把bool结果传给参数lambda,这个lambda用这个bool和其他三个lambda调用Conditional,Conditional决定把第三个lambda传给前两个中的哪个,假设它选择了传给alternative,那么,D执行然后把他的结果传给continuation,也就是t=>M(currentContinuation, t) ,然后M用这个整数t做一些事情,再调用之前说过的currentContinuation,然后就OK了。

没有返回,每个方法都是void的,每个委托(匿名函数,lambda)都是一个Action,函数最后做的事情就是调用其他函数,调用的时候前面的调用栈已经没用了,因此编译器可以优化之前的代码,使得没有任何函数会消耗调用栈的空间,值全都临时存储在寄存器之类的临时位置。

也许你会觉得这TM是什么烂代码?但是你看,我们已经做出了一个CPS版本的?:运算符啊,行为完全与之相同,有用lambda实现的惰性求值,控制流由继续向最后面的continuation传合适的方法得以继续。

然而该用continuation解决的事我们一个都没干。

Continuation是控制流的异化,目前我们只讨论了一个单continuation传来传去的系统,因为continuation只是一个委托(lambda),所以我们没有理由只在系统里面用一个continuation。

下一节:一些关于更复杂的控制流的冥想。

CPS冥想 - 1 重新审视CPS的更多相关文章

  1. CPS冥想 - 2 手撸控制流

    原博客链接:http://blogs.msdn.com/b/ericlippert/archive/2010/10/22/continuation-passing-style-revisited-pa ...

  2. CPM、CPC、CPA、CPS、CPL、CPR 是什么意思 -解析互联网广告术语

    CPA CPS CPA/CPS常见的推广方式 CPA和CPSCPA,CPS CPS与CPA CPA.CPSCPA.CPS产品教  CPA CPS什么意思 CPACPS是什么 1. CPM(Cost p ...

  3. 解密自动CPS变换

    7.2 1 前言 我最一开始听到 CPS 变换这个词是在王垠的博客里 (请求不要喷我),就是那篇他第一次宣传他的40行代码的文章. 我当时什么都看不懂,所以没太注意,不过我也正在学程序语言方面的东西, ...

  4. CPS(Cyber-Physical Systems)白皮书-摘选

    <中国制造2025>提出,“基于信息物理系统的智能装备.智能工厂等智能制造正在引领制造方式变革”,要围绕控制系统.工业软件.工业网络.工业云服务和工业大数据平台等,加强信息物理系统的研发与 ...

  5. 栈编程和函数控制流: 从 continuation 与 CPS 讲到 call/cc 与协程

    原标题:尾递归优化 快速排序优化 CPS 变换 call/cc setjmp/longjmp coroutine 协程 栈编程和控制流 讲解 本文为部分函数式编程的扩展及最近接触编程语言控制流的学习和 ...

  6. Jmeter模拟不同带宽

    Jmeter自带模拟带宽设置,当然前提肯定是你当前的带宽>=你要模拟的带宽,好比你装了个4m的宽带,要模拟100m的带宽,那是做梦 做起来也不难,打开user.properties文件,增加如下 ...

  7. proxool 连接池警告分析:appears to have started a thread named [HouseKeeper] but has failed to stop it

    1. 问题:日志中出现下面的警告: 警告: The web application [ROOT] appears to have started a thread named [HouseKeeper ...

  8. 使用SQL联合查询来构建临时vo对象的应用

    联合查询: 表1: team球队表 表2:schedule 赛程表 需要数据: 球队名称.主队ID.主队名称.客队ID.客队名称.胜负情况 方法1. Object数组取出列和数值 import jav ...

  9. C# 对动态编辑的一些学习笔记

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Comp ...

随机推荐

  1. Windows Store App 控件动画

    在开发Windows应用商店应用时,开发工具中已经封装了大量的控件供开发人员使用,而其中有一部分控件,例如FlipView.ToolTip.ListView以及SemanticZoom等控件中已经默认 ...

  2. java_easyui体系之DataGrid(3)[转]

    一:简介 在2的基础上实现增删改.增.改都是通过行编辑来实现的.具体业务逻辑: 1. 增加一条记录的时候: a) 在datagrid表格最前端增加一行(当然也可在指定位置增加.)增加的行的字段必须与要 ...

  3. 9patch边框黑线的含义

    上面黑线或者点表示纵向可拉伸的区域 一般一个点即可 左边黑线或者点表示横向可拉伸的区域 一般一个点即可 下面表示纵向填放内容的区域  一般是一条黑线 右边表示横向填放内容的区域  一般是一条黑线

  4. iOS开发多线程篇—NSOperation基本操作

    iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. ...

  5. IT行业的斗争

    有朋友和我说,她希望做一名IT工作者,原因是可以对着机器工作,不需要再面对人与人之间的是是非非. 我真心不想打破她的憧憬,因为无论干任何行业,有人,就有矛盾,就有斗争. 我是那名失败者,仅仅想安安稳稳 ...

  6. JavaScript的this用法

    1. 全局代码中的this this在全局上下文中,它的值是全局对象本身(Global Object),在浏览器中就是Window  Object,如下图示. 看下面几个例子: //Global sc ...

  7. android 第一个程序的编写

    移通152余继彪 需求分析:输入两个数字,让他们相乘,然后得出结果 首先建立一个android项目 在 layout中建立第一个界面 该界面有四个组件,两个editText 一个TextView,一个 ...

  8. bootstrap学习<三>打开模态窗体

    可以切换模态框(Modal)插件的隐藏内容: 通过 data 属性:在控制器元素(比如按钮或者链接)上设置属性 data-toggle="modal",同时设置 data-targ ...

  9. 删除ubuntu旧版本内核

    方法一: 1.查看系统下可使用的内核有哪些 dpkg --get-selectiongs|grep linux-image liming@CM:~$ dpkg --get-selections|gre ...

  10. HDFS中的checkpoint( 检查点 )的问题

    1.问题的描述 由于某种原因,需要在原来已经部署了Cloudera CDH集群上重新部署,重新部署之后,启动集群,由于Cloudera Manager 会默认设置dfs.namenode.checkp ...