一日,你查看你的程序代码,你有两大块代码看起来几乎完全的一样。事实上它们就是完全一样,除了一个代码里说的是“Spaghetti(意大利面条)”,另一个代码里说的是“Chocolate Moose(巧克力慕丝)”。
// 一个小例子: alert("I'd like some Spaghetti!"); alert("I'd like some Chocolate Moose!");
这个例子恰好是用Javascript写的,但即使是你不懂Javascript,你也应该能看懂我说的。
当然,重复的代码看起来不太好。所以你决定写一个函数:
function SwedishChef( food ) { alert("I'd like some " + food + "!"); } SwedishChef("Spaghetti"); SwedishChef("Chocolate Moose");
没错,这个例子很简单,但你可以想出一些更有实际价值的例子。这样做是更好一些,有很多理由,这些理由估计你都听说过一万遍了。可维护性,可读性,抽象 = 好!
现在,你又发现两块代码几乎完全一样,除了一块是不停的调用一个叫BoomBoom的函数,而一块是不停的调用一个叫PutInPot的函数。除此之外,这两块代码完全一样。
alert("get the lobster"); PutInPot("lobster"); PutInPot("water"); alert("get the chicken"); BoomBoom("chicken"); BoomBoom("coconut");
现在,你需要一个途径,把一个参数传递到一个函数里,而这个参数本身是个函数。这是一个很重要的功能,它是一个好的方法,能让你发现函数中存在的重复的代码,减少这样的重复。
function Cook( i1, i2, f ) { alert("get the " + i1); f(i1); f(i2); } Cook( "lobster", "water", PutInPot ); Cook( "chicken", "coconut", BoomBoom );
看见了没!我们把一个函数当做了参数。
你的语言能这样做吗?
且慢… 如果你还没有写出PutInPot 或 BoomBoom 函数呢。如果你能把他们写成内联函数,而不是要在其它地方先声明,这样是不是更好?
Cook( "lobster", "water", function(x) { alert("pot " + x); } ); Cook( "chicken", "coconut", function(x) { alert("boom " + x); } );
老天,这太方便了。注意到了没有,我即时创建了一个方法,甚至都不用麻烦给它起名,只需掂着它的耳朵把它丢进函数里。
当你开始思考把匿名函数当作参数时,你也许会注意到有一种代码到处都是,就是,遍历数组里的所有元素进行操作。
var a = [1,2,3]; for (i=0; i<a.length; i++) { a[i] = a[i] * 2; } for (i=0; i<a.length; i++) { alert(a[i]); }
对数组里的每个元素进行操作是一种很常见的动作,你可以写出一个函数,让它为你做这些:
function map(fn, a) { for (i = 0; i < a.length; i++) { a[i] = fn(a[i]); } }
现在,你可以把上面的代码重写成这样:
map( function(x){return x*2;}, a ); map( alert, a );
另一个常见的跟数组相关的操作是,通过某种方式把数组里的所有值组合到一起。
function sum(a) { var s = 0; for (i = 0; i < a.length; i++) s += a[i]; return s; } function join(a) { var s = ""; for (i = 0; i < a.length; i++) s += a[i]; return s; } alert(sum([1,2,3])); alert(join(["a","b","c"]));
sum 和 join 看起来非常的相似,你也许会想把它们的通用之处提取出来做成一个能把数组里的元素合并成一个值的通用函数:
function reduce(fn, a, init) { var s = init; for (i = 0; i < a.length; i++) s = fn( s, a[i] ); return s; } function sum(a) { return reduce( function(a, b){ return a + b; }, a, 0 ); } function join(a) { return reduce( function(a, b){ return a + b; }, a, "" ); }
很多老式的语言根本没有方法做出这种事情。另外一些语言允许你做这些,但不容易(例如,C语言里有函数指针,但你必须进行声明,并要在什么地方定义它)。面向对象的语言并没有被证实可以允许你对函数做所有的操作。
如果你想在Java里把函数作为一个一等(First Class)对象,你需要建一个只包含一个用来调用功能点的方法的整个对象。把这种现象跟实际情况联系起来,很多的面向对象语言都会要求你为每个class创建一个完整的文件,非常的没效率。如果你的编程语言里要求你去这样的调用功能点,那你根本没有享受到现代语言环境给你带来的所有好处。看看能否退货吧,挽回一点损失。
写这样的小函数,只是做一些遍历数组,处理其中的每个元素的操作,这样做究竟能得到多少好处?
那好,我们来回头看一看map这个函数。当你需要对数组里的每个元素依次做一些操作时,实际情况是,你并不在乎处理这些元素的顺序。你可以向前或向后遍历整个数组,得到的结果是一样的,不是吗? 事实上,如果你的机器是2cpu的,你可以写出一些程序让每个cpu个处理一半的元素,你的map一下子就变快了2倍。
或者,只是个假设,在你遍布全球的数个数据中心里,你有成千上万的服务器,你有一个非常非常大的数组,我说过,只是假设,它们装载着整个互联网的内容信息。那现在,你就可以在你的成千上万的计算机上运行map函数,每个机器都能分摊掉计算中的一小部分任务。
所以,如今,举个例子,要想写出一个十分高效的能搜索整个互联网内容信息的代码,你只需要简单的用基本搜索字符串当作参数来调用map函数就行了。
这里,我想请你们要真正注意的有趣的事情是,你会发现像map 和 reduce这样的函数每个人都可以使用,当人们使用它时,你只需要找到一个编程能手写出最困难的调用map 和 reduce 函数的代码,让它们能够运行在全球大量的并行执行的计算机上,而以前旧的运行的很好的代码只需要调用这个循环操作,唯一不同的是,它们获得了比以前千万倍快的速度,这意味着你能做瞬间处理完巨大的计算工作。
让我再复述一遍。通过把通用的循环操作提取出来,你可以实现你想要的任何循环操作,包括实现出一种能随硬件设备的增加而性能升级的效果。
我想现在你就该明白为什么我在前段时间写的一篇文章里抱怨学校只教授计算机科学专业的学生Java知识而忽略其它:
缺乏对函数式编程的理解,你不可能发明出MapReduce——这个能够让Google实现大规模按需扩展和升级的算法。Map和Reduce这两个词来自于Lisp语言和函数式编程。回首看来,MapReduce对于任何还存有记忆的人来说都意味着一种纯函数式的编程,没有副作用,易于并行计算。事实恰巧是Google发明了MapReduce,而微软没有,这就说明了为什么微软仍然努力做那些基本的搜索功能研究的原因了,而Google已经开始了它的下一个目标:开发它的Skynet^H^H^H^H^H^H——这世界上最大规模的并行超级计算机。我并不觉得微软已经认识到在如今的潮流中它已经落后的多远。
那么,我希望现在你已经能理解了以函数为一等(First class)特征编程语言能使你更容易的对代码进行提炼抽象,这意味着你的代码更短小,紧凑,可复用性强,更容易扩展升级。大量的Google应用程序都使用了MapReduce,在他们优化程序或修改Bug时,都能从中得到益处。
现在我要说一点怨言牢骚,最高效的语言开发环境应该是一种能让你在不同层次上进行抽象归纳的语言环境。笨拙陈旧的FORTRAN语言甚至不允许你写函数。C语言里有函数指针,但实现的很丑陋,不能匿名,使用之前必须先进行声明实现。Java允许你使用功能点调用(functor),但更加丑陋。就像Steve Yegge指出的,Java就是一个名词的王国。
纠正:我最后一次使用FORTRAN大概是27年前,很显然它现在有了函数了。我还当是GW-BASIC呢
相关推荐
编程语言转换器 编程语言转换器 C# c++ java VB 相互转换的工具
编程语言自动代码转换器 编程语言自动代码转换器!!!!!!!!!!!!!!!!!!!!!!!
十进制八进制转换,十进制八进制转换,十进制八进制转换,
PLC的5种编程语言 1)顺序功能图SFC(Sequential Function Chart) 用来设计顺序控制程序,是位于其它语言之上的图形语言 现在它实际上是一种组织编程的工具(相当于流程图),还必须用其它语言(如梯形图)转换为...
C 语言编程常见问题解答 【作者】[美]Paul S.R. Chisholm 译:张芳妮 吕 波 【出版社】清华大学出版社 C语言编程常见问题解答(目录) 第l章 C语言 1. 1 什么是局部程序块(local block)? 1. 2 可以把变量保存...
FX 三菱PLC 结构化文本ST 语言与 梯形图混合编程模板, 使用 WORKS2 编写. 内有 结构化文本ST语言 编程手册 和 模板实例文件, 学ST 语言入门的非常推荐.
有一个魔王总是使用自己的一种非常精练面抽象的语言讲话,没有人能听得慌,但他的语言是可以逐步解释成人能听懂的语言,因为他的语言是由以下两种形式的规则由人的语言逐步抽象上去的,进而转换成人类语言。 设计一个...
编程语言java类型转换.pdf
编程语言javadoc转chm.pdf
Scratch 是可视化的编程语言,其丰富的学习环境适合所有年龄阶段的人。利用它可以制作交互式程序、富媒体项目,包括动画故事、读书报告、科学实验、游戏和模拟程序等。《动手玩转Scratch2.0编程—STEAM创新教育指南...
掌握VC++语言和汇编语言的混合编程方法,了解不同编程语言的接口方法,体会汇编语言的应用。 掌握嵌入汇编函数和汇编语言子程序与VC++的混合编程方法,入口、出口参数的传递方法以及在VC++环境下混合编程的调试方法...
Scratch用五颜六色的命令块和卡通精灵来创建功能强大的脚本,而不是使用晦涩的在编程语言和难懂的大量行代码。在学习Scratch编程过程中,作者Majed Marji使用Scratch解释现实生活中所遇到的编程问题的概念和方法。...
从VFP数据库编程转Visual 2010 C# 数据库编程的示例,内有完整的实例。在Visual 2010 C#上调试通过。
编程语言-类层次结构抽象描述语言的设计及其到C++的转换.pdf
内容概要:本教程将指导读者如何使用R语言进行图片批量转换为PDF文件的编程实践。通过详细的步骤和代码示例,读者将学习如何读取文件夹中的图片文件、使用R语言的图形和PDF处理功能进行格式转换,并最终批量生成PDF...
R语言初步_统计绘图与编程,主要用于统计分析、数据挖掘等可视化
用zig 编程语言编写的简单俄罗斯方块克隆 。 控件 左/右/下箭头 - 左/右/下移动棋子。 向上箭头 - 顺时针旋转部件。 Shift - 逆时针旋转工件。 空间 - 立即放下一块。 左 Ctrl - 按住棋子。 R - 开始新游戏。 P - ...
C/C++语言经典实用趣味程序设计编程百例精解 <br>C/C++语言经典实用趣味程序设计编程百例精解(1) <br>1.绘制余弦曲线 2.绘制余弦曲线和直线 3.绘制圆 4.歌星大奖赛 5.求最大数 6.高次方数...
公历(阳历) 农历(阴历)转换,支持时间段从 1900-2100 ...支持各种编程语言 C#,java,Objective-C,php,Python,javascript(nodejs),C/C++,ruby,swift,golang 等 支持 Mac,Windows,Android,WP 多种平台
如果你做得好,Perl 也能帮你这个忙。 <br/>Perl 还可以帮你写更安全的程序。除了其它语言提供的典型的安全接口之外,Perl 还通过一种跟踪数据的机制给你提供预防意外安全错误的保护,这样就可以在灾害发生之前...