10.3.2 利用连续的代码

问题是,我们既希望做尾递归调用,还要在尾递归调用完成后,再执行一些代码。这看起来是一个棘手的问题,但有一个有趣的解决方案。我们把要在递归调用完成后执行的所有代码,拿来作为参数值,提供给递归调用,这样,我们要写的函数将仅只包含一个递归调用。

可以把这个看作是另一种类型的累加器参数:我们不是累加值,而是累加“需要在以后运行的代码”。现在的问题是,我们如何能拿到其余的代码,并把它作为给函数的参数值?这可能要感谢一等函数(first class functions),这个最后的参数就称为连续(continuation),因为它指定运行应该如何继续。

在看过一些实际的例子后,这一切会变得更清楚。清单 10.17 是一个简单的函数,首先,以通常的风格实现,然后,使用连续。这里,我们将使用 C#,只有一个新的概念要理解,但请记住,C# 不支持尾递归:在 C# 中,这个技术不能作为递归的优化而使用。(在 C# 中连续仍然可用,只是不是为递归的。)

清单 10.17 用连续写代码(C#)

// Reports result as return value

int StringLength(string s) {

returns.Length;

}

void AddLengths() {     [1]

int x1= StringLength("One");

int x2= StringLength("Two");

Console.WriteLine(x1+ x2);

}

// Reports result using continuations

void StringLengthCont(string s,Action<int> cont) {

cont(s.Length);     [2]

}

void AddLengthsCont() {

StringLengthCont("One", x1 =>     [3]

StringLengthCont("Two", x2 =>    [4]

Console.WriteLine(x1 + x2)

));

}

在两个版本中,我们首先声明了计算字符串长度的函数。按通常的编程风格,把结果作为返回值;当使用连续时,我们增加了一个函数(连续)作为最后一个参数值。要返回结果,StringLengthCont 函数调用这个连续[2]。我们将使用函数代替通常的 return 语句,这样,值是作为给函数的参数值 ,而不是把它作为结果存储在栈中。

下一个函数,AddLengths,[1]计算两个字符串的长度,再把这两个值加起来,再输出结果。在使用连续的版本中,只包括一个顶层对StringLengthCont 函数的调用[3]。调用的第一个参数值是字符串,第二个参数值是连续。顶层的调用是函数要做的最后事情,因此,在 F# 中,可以使用尾调用运行,它不会占用任何栈空间。

连续接收第一个字符串的长度作为参数值;在内部,再为第二个字符串调用StringLengthCont。另外,把连续作为最后参数值给它,它只被调用一次,我们可以计算两个长度的和,并输出结果。在 F# 中,在连续内部的调用[4]还是尾调用,因为它是代码在 lambda 函数中所要做的最后工作。现在,我们要看一下使用这种编程风格,来优化我们以前的函数,计算树中元素的和。

时间: 2024-10-10 17:59:47

10.3.2 利用连续的代码的相关文章

图片左右循环连续滚动代码,解决marquee的留白问题

<marquee ONMOUSEOUT="this.start()" ONMOUSEOVER="this.stop()" DIRECTION="LEFT" scrollamount=3 behavior="scroll" loop="-1" deplay="0"> <table> <tr> <td> <a href="htt

专心10年,写ASP.NET代码开始,从0到拥有5系

工作到现在,几乎就没离开过这里,写这篇文章也是给自己这10年做个总结,也希望能给选择了程序员这个职业的朋友,带来一些激励与感悟,写这文章又不想影响到自己的工作与生活,所以专门注册了一个马甲帐号请谅解,我也是园子的常客,有写过很多博文. 起点高低不会左右你的人生在这个职业里.我的同事与朋友里,好像我的起点还真是挺低的,高中辍学,妈妈看了我这样白上了10年学,到工地打工浪费了,拿出了家里仅有的5000元报了一个职业学校,学了一年的计算机技术,PHOTOSHOP,网页制作三剑客,还有后来ASP语言.在

10分钟,利用canvas画一个小的loading界面(顺便讨论下绘制效率问题)

首先利用定义下canvas得样式 <canvas width="1024" height="720" id="canvas" style="border: 1px solid #808080;display: block;margin: 100px auto;>你的游览器不支持canvas</canvas> 这里主要要说的就是宽高,不要在style里面定义,不然会被拉伸.(对于这点,建议大家看下W3c文档,不是很

10分钟,利用canvas画一个小的loading界面

首先利用定义下canvas得样式 <canvas width="1024" height="720" id="canvas" style="border: 1px solid #808080;display: block;margin: 100px auto;>你的游览器不支持canvas</canvas> 这里主要要说的就是宽高,不要在style里面定义,不然会被拉伸.(对于这点,建议大家看下W3c文档,不是很

在Mac OS X上利用Spotlight搜索代码

用grep来搜代码和方便,最原生态了,常用的: ? ~ grep 'NuPlayer' -iInr --color ./aosp 其中`i`表示大小写忽略:`I`表示忽略binary文件:`n`显示搜索结果的行号:`r`表示递归搜索子目录 不过grep搜索有点慢,网上也很多通过建立索引加快搜索速度的方法:"other grep-like tools and indexing tools" 一向用户体验著称的Mac指定不会缺少这个,`Spotlight`就是系统自带的基于索引搜索工具,使

Android开发实践:利用ProGuard进行代码混淆

由于Android的代码大都是Java代码,所以挺容易被反编译的,好在Android ADT为我们集成了混淆代码的工具,一来可以混淆我们的代码,让程序被反编译后基本看不懂,另外还能起到代码优化的作用.发布项目前,建议打开Android的代码混淆功能. Android ADT主要通过ProGuard工具来提供代码混淆,网上也有挺多博客文章讲这个的,但感觉很多都介绍得太过于复杂,这里我就以问答的方式来更加简洁地介绍下ProGuard吧. 1. ProGuard是什么 ProGuard是一个工具,用来

分针网——每日分享:10个程序员常用的代码简写技术

更多文章:www.f-z.cn 今天小编我给大家整理了一份10个程序员常用的代码简写技术,看懂一种是入门,全懂就是大神,你能知道几个呢? 1.三元操作符 当想写if...else语句时,使用三元操作符来代替. const x = 20;let answer;if (x > 10) { 简写: const answer = x > 10 ? 'is greater' : 'is lesser'; 也可以嵌套if语句: const big = x > 10 ? " greater

推荐10个非常实用的PHP代码片段

当使用PHP进行开发的时候,如果你自己收藏 了一些非常有用的方法或者代码片段,那么将会给你的开发工作带来极大的便利.今天我们将介绍10个超级好用的PHP代码片段,希望大家能够喜欢! 1.  使用textmagic API发送消息 可能有的时候,你需要发送一些短信给你的客户,那么你绝对应该看看textMagic.它提供了非常简单的API来实现这个功能.但是不是免费的. // Include the TextMagic PHP lib require('textmagic-sms-api-php/T

css 10 款非常棒的CSS代码格式化工具推荐

http://www.iteye.com/news/23692/  10 款非常棒的CSS代码格式化工具推荐 2011-12-14 09:31 by 副主编 wangguo 评论(0) 有9111人浏览 CSS 代码格式化 相关知识库:  PHP知识库  信息无障碍知识库  计算机网络知识库  C#知识库 如果你刚开始学习CSS,这意味着你的很多代码或结构可能需要优化,比如你可能过多使用了类.添加了多余的间隔或空行等等,这将导致代码臃肿.混乱,可读性和执行效率将大大降低. 本文为你整理了几款CS