一日,你查看你的程序代码,你有两大块代码看起来几乎完全的一样。事实上它们就是完全一样,除了一个代码里说的是“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就是一个名词的王国。
分享到:
相关推荐
由Java说起:编程语言还需要开源吗
在讨论西门子安全控制器的编程语言时,首先需要明确的是安全控制器(F-CPU)与常规控制器的区别。安全控制器的主要职责是在工业环境中负责安全功能,它们被设计来减少因设备故障而导致的安全风险。编程这些安全控制器...
仓颉编程语言语言规约.pdf
C++是一种核心编程语言,它的地位在编程世界中至关重要,特别是在系统级编程、高性能计算、游戏开发和金融建模等领域。C++源自C语言,它不仅保留了C语言的低级控制特性,还引入了面向对象编程(OOP)的概念,如类、...
"自制编程语言相关资料" 本资源摘要信息是关于自制编程语言的相关资料,涵盖了编程语言的设计、实现和使用等方面。下面是从给定的文件中提取的知识点: 编程语言设计 * 编程语言的设计是指根据一定的规则和规范...
根据提供的标题“编程语言原理(第10版)”及描述“编程语言原理(第10版 编程语言原理)”,我们可以推断出这本书主要讲述了编程语言的基础理论与实践应用,是学习和理解编程语言设计与实现的重要参考资料。...
为了适应这种情况,Zemax提供了一种专有的编程语言——Zemax编程语言(ZPL),让设计师们能够更深入地开发和利用Zemax软件。 Zemax编程语言(ZPL)是一种专门为光学设计软件Zemax设计的脚本语言。它允许用户编写...
如果编程语言里面的所有英语单词你都不认识,那么很难想像你怎么可能学会编程语言。 特别是当看别人的源码或自己写源码时,会用到很多的英语单词来命名变量名称、类名称、字段名称等的,如果单词量达不到的话,会...
ST语言,全称为Structured Text,是IEC 61131-3标准下的编程语言之一,常用于工业控制领域的PLC(可编程逻辑控制器)编程。ST语言以其丰富的表达能力和接近高级编程语言的语法特性,深受工程师们的青睐。本手册全面...
通过阅读《编程语言原理(第10版)》,读者不仅可以掌握编程语言的核心概念,还能深入了解语言设计的底层机制,从而具备分析、理解和设计新编程语言的能力。这不仅对软件开发人员,也对教学和研究工作具有深远的影响。
编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集...
**Dart编程语言详解** Dart是一种由Google开发的面向对象、高性能的编程语言,它设计用于构建现代的Web和移动应用程序。Dart语言自2011年发布以来,已经在开发社区中获得了广泛的认可,尤其在Flutter框架的推动下,...
工业机器人的编程语言是机器人技术领域的重要组成部分,随着工业机器人的发展,编程语言也在不断地发展和完善。机器人的编程主要可以分为示教编程、动作级编程语言和任务级编程语言三个层次。在开发这些机器人时,...
自学编程,你需要选择一种编程语言开始,比如Python、Java或C++,这些语言都有丰富的在线资源,包括教程、文档和社区支持。 第一步,建立基础知识。你需要学习编程基础概念,如变量、数据类型、控制结构(如循环和...
编程语言、技术问题解决方案和使用经验编程语言、技术问题解决方案和使用经验编程语言、技术问题解决方案和使用经验编程语言、技术问题解决方案和使用经验编程语言、技术问题解决方案和使用经验编程语言、技术问题...
编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言算法集/Rust编程语言...
Plant Simulation编程语言SimTalk 2.0官方说明 Plant Simulation编程语言SimTalk 2.0是Tecnomatix Plant Simulation软件中的一种编程语言,用于扩展模拟模型的功能和控制。SimTalk语言可以与物流对象和信息流对象...
世界上第一门编程语言究竟是谁? 编程语言是计算机的灵魂,它存在感远远高于计算机,是纽带把我们和计算机深深的联系在了一起。那么,世界上第一门编程语言究竟是谁?今天,我们将穿越到计算机和编程语言的起点,去...