From The Joel on Software Translation Project
<!-- start content -->
你的编程语言可以这样做吗?
有一天,你在浏览自己的代码,发现有两大段代码几乎一样。实际上,它们确实是一样的——除了一个关于意大利面(Spaghetti)而另一个关于巧克力慕思(Chocolate Moose)。
// 一个小例子:
alert("偶要吃意大利面!");
alert("偶要吃巧克力慕思!");
嗯,这个例子碰巧是用javascript写的,不过你就算不懂JavaScript,应该也能明白它在干什么。
拷贝代码不好。于是,你创建了个函数
function SwedishChef( food )
{
alert("偶要吃" + food + "!");
}
SwedishChef("意大利面");
SwedishChef("巧克力慕思");
Ok,这只是一个很小很小的例子而已,相信你能想像到个更实际一点的例子。这段代码有很多优点,你全都听过几万次了:可维护性、可读性、抽象性 = 好!
现在你留意到有另外两段代码几乎跟它们一模一样,除了一个反复调用一个叫BoomBoom的函数,另一个反复调用一个叫PutInPot的。除此之外,這两段代码简直没什么两样:
alert("拿龙虾");
PutInPot("龙虾");
PutInPot("水");
alert("拿鸡肉");
BoomBoom("鸡肉");
BoomBoom("椰子酱");
现在要想个办法,使得你可以将一个函数用作另一个函数的参数。这是个重要的能力,因为你更容易将框架代码写成一个函数(emu注:还记得template method模式吧?)。
function Cook( i1, i2, f )
{
alert("拿" + i1);
f(i1);
f(i2);
}
Cook( "龙虾", "水", PutInPot );
Cook( "鸡肉", "椰子酱", BoomBoom );
看看,我们居然把函数当成调用参数传递了!
你的编程语言能办到吗?
等等……假如我们已经有了PutInPot和BoomBoom这些函数的具体实现代码(而且又不需要在别的地方重用它们),那么用内联语法把它们写进函数调用里面不是比显式的声明这两个函数更漂亮吗?
Cook( "龙虾",
"水",
function(x) { alert("pot " + x); } );
Cook( "鸡肉",
"椰子酱",
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要求你建立一个有单方法的对象,称为算子对象。许多面向对象语言要你为每个类都建立一个完整文件,像这样开发可真叫快。如果你的编程語言要你使用算子对象来包装方法(而不是把方法本身当成对象),你就不能徹底得到现代(动态)编程语言的好处。不妨试试看你可否退货拿回些钱?
不用再写那些除了经过一个数组对每个元素做一些事情之外一无是处的函数,有什么好处?
让我们看回map函数。当你要对数组内的每个元素做一些事,你很可能不在乎哪个元素先做。无论由第一个元素开始执行,还是是由最后一个元素执行,你的结果都是一样的,对不?如果你手头上有2個CPU,你可以写段代码,使得它们各对一半的元素工作,于是乎map快了两倍。
或者,发挥一下想像力,设想你在全球有千千万万台服务器分布在全世界的若干个数据中心,你有一个真的很大很大的数组,嗯,再发挥一下想像力,设想这个数组记录有整个互联网的内容。还了,现在你可以在几千台服务器上同时执行map,让每台服务器都来解决同一个问题的一小部分。
那么在这个例子里面,编写一段非常快的代码来搜索整个互联网这个问题,其实就和用一个简单的字符串搜索器(算子)作为参数来调用map函数一样简单了。
希望你注意到一个真正有意思的要点,如果你想要把map/reduce模式变成一个对所有人都有用,对所有人都能立刻派上用场的技术,你只需要一个超级天才来写最重要的一部分代码,来让map/reduce可以在一个巨大的并行计算机阵列上运行,然后其他旧的但是一向在单一个循环中运行良好的代码,仍可以保持正确的运行,惟一的差别只是比原来单机运行快了n倍。这意味着它们都一不留神突然变成可以被用来解决一个巨大的问题的代码。
让我再啰嗦一下,通过把“循环”这个概念加以抽象,你可以把用任何你喜欢的方式来实现“循环”过程,包括可以实现让循环迭代速度随着硬件计算能力保持令人满意的同步增长。
你现在应该可以明白不久为何对那些对除了Java之外什么都沒被学过的计算机系学生表示不满了: (http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html):
不理解函数式编程,你就发明不了像MapReduce这样让Google的计算能力如此具有可扩展性的算法。Map和Reduce这两个术语源自Lisp语言和函数式编程.MapReduce概念对于任何还能记得他们的6.001-equivalent编程课上讲过“真正的函数式的程序应该没有任何副作用,可以轻易并行运行”的人来说是非常容易理解的。Google发明了MapReduce而微软没有,这一定程度上可以解释了为什么在google已经转下了他们的下一个目标(建设世界上最大型的超级并行计算机阵列Skynet)的时候微软还在想方设法让他们的最基础的搜索程序跑起来。我不觉得微软能完全了解在这一波浪潮中他们落后了多远。
我希望你现在明白,把函数当成基本类型的(动态)编程语言能让你在编程过程中更好的进行抽象化,也就是使代码精悍、功能更内聚、更具可重用性及更具有扩展性。很多的Google应用使用Map/Reduce模式,因此一有人对其优化或修正缺陷,它们就都可以从中得益。
我准备要再罗嗦一下,我认为最有生产力的编程语言莫过于能让你在不同层次上都可以进行抽象化的。老掉牙的FORTRAN 语言以前是不让你写函数的注。C 有函数指针,可是它们都非常丑丑丑丑丑丑丑丑陋,不允许匿名声明,又不能在用它们时实现它们而偏偏要放在別处去实现。Java让你使用算子对象,一种更丑陋的东西。正如Steve Yegge所述,Java是個名词王国(http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)。
作者注:这里提起了FORTRAN,不过我上次使用FORTRAN是27年前的事了。FORTRAN是有函数的,我码字那会儿脑子里面想的大概是GW-BASIC语言。(emu注,basic确实只有所谓的子程序和go-sub语句,作用只是重新组织代码结构而已,没有参数和调用堆栈,因此没有真正的函数调用)
相关推荐
由Java说起:编程语言还需要开源吗
Python编程语言能做什么软件?.docx
Python编程语言能做什么软件?.pdf
在讨论西门子安全控制器的编程语言时,首先需要明确的是安全控制器(F-CPU)与常规控制器的区别。安全控制器的主要职责是在工业环境中负责安全功能,它们被设计来减少因设备故障而导致的安全风险。编程这些安全控制器...
C++是一种核心编程语言,它的地位在编程世界中至关重要,特别是在系统级编程、高性能计算、游戏开发和金融建模等领域。C++源自C语言,它不仅保留了C语言的低级控制特性,还引入了面向对象编程(OOP)的概念,如类、...
.Python和。。scratch编程语言哪些更适合新手?.docx
.Python和。。scratch编程语言哪些更适合新手?.pdf
仓颉编程语言语言规约.pdf
"自制编程语言相关资料" 本资源摘要信息是关于自制编程语言的相关资料,涵盖了编程语言的设计、实现和使用等方面。下面是从给定的文件中提取的知识点: 编程语言设计 * 编程语言的设计是指根据一定的规则和规范...
通过Scratch的学习,孩子们可以逐渐建立起程序设计的思维模式,这不仅对将来学习更高级的编程语言,如Python、Java等有积极的帮助,而且能够帮助他们更好地适应数字化时代的需求。 此外,Scratch的社区功能也是其...
根据提供的标题“编程语言原理(第10版)”及描述“编程语言原理(第10版 编程语言原理)”,我们可以推断出这本书主要讲述了编程语言的基础理论与实践应用,是学习和理解编程语言设计与实现的重要参考资料。...
### PLC标准编程语言之一ST语言 #### 一、概述 结构化文本(Structured Text, ST)作为IEC 61131-3标准中定义的一种编程语言,为自动化控制系统提供了一种高级语言的编程方式。相较于传统的梯形图语言,ST具有更高...
如果编程语言里面的所有英语单词你都不认识,那么很难想像你怎么可能学会编程语言。 特别是当看别人的源码或自己写源码时,会用到很多的英语单词来命名变量名称、类名称、字段名称等的,如果单词量达不到的话,会...
Plant Simulation编程语言SimTalk 2.0官方说明 Plant Simulation编程语言SimTalk 2.0是Tecnomatix ...SimTalk 2.0语言是Plant Simulation软件中的一种强大且灵活的编程语言,可以帮助用户更好地模拟和控制模拟模型。
ST语言,全称为Structured Text,是IEC 61131-3标准下的编程语言之一,常用于工业控制领域的PLC(可编程逻辑控制器)编程。ST语言以其丰富的表达能力和接近高级编程语言的语法特性,深受工程师们的青睐。本手册全面...
汇编语言是一种面向机器的编程语言,它可以直接利用计算机硬件特性并能通过汇编指令直接控制机器硬件。因此,使用汇编语言可以编写出在时间和空间上效率最优的程序。然而,汇编语言的学习并不是一件容易的事,它需要...
Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 ...
机器人的编程主要可以分为示教编程、动作级编程语言和任务级编程语言三个层次。在开发这些机器人时,常常使用C、C++、C++Builder、VB、VC等计算机语言,主要取决于其执行机构(伺服系统)的开发语言。机器人编程语言...
编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集...
标题:CFC编程语言 描述:本文旨在深入探讨CFC编程语言的应用、特性及其实例讲解,为读者提供全面的了解和实践指南。 ### CFC编程语言概览 CFC(Continuous Function Chart)编程语言是一种图形化的编程工具,...