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语句,作用只是重新组织代码结构而已,没有参数和调用堆栈,因此没有真正的函数调用)
相关推荐
在讨论西门子安全控制器的编程语言时,首先需要明确的是安全控制器(F-CPU)与常规控制器的区别。安全控制器的主要职责是在工业环境中负责安全功能,它们被设计来减少因设备故障而导致的安全风险。编程这些安全控制器...
仓颉编程语言:一文读懂,什么是仓颉编程语言? 仓颉编程语言:一文读懂,什么是仓颉编程语言? 仓颉编程语言:一文读懂,什么是仓颉编程语言? 仓颉编程语言:一文读懂,什么是仓颉编程语言? 仓颉编程语言:一文读...
仓颉编程语言语言规约.pdf
”这个问题引发了一场关于编程语言原理的探讨。 在计算机科学的历史中,编程经历了多次演变。从早期的机械计算,到莱布尼兹的逻辑语言设想,再到雅卡尔的可编程织布机和Ada Lovelace的循环与子程序概念,编程的雏形...
仓颉编程语言:什么是仓颉编程语言,它的特点是什么? 仓颉编程语言:什么是仓颉编程语言,它的特点是什么? 仓颉编程语言:什么是仓颉编程语言,它的特点是什么? 仓颉编程语言:什么是仓颉编程语言,它的特点是...
"自制编程语言相关资料" 本资源摘要信息是关于自制编程语言的相关资料,涵盖了编程语言的设计、实现和使用等方面。下面是从给定的文件中提取的知识点: 编程语言设计 * 编程语言的设计是指根据一定的规则和规范...
编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜编程语言排行榜
《自制编程语言基于C语言郑钢源代码》的项目,是一项富有挑战性的技术实践,旨在帮助开发者理解编程语言的底层工作原理,以及如何...通过这个项目,你可以亲身体验到编程语言的奥秘,从而在软件开发的道路上更进一步。
为了适应这种情况,Zemax提供了一种专有的编程语言——Zemax编程语言(ZPL),让设计师们能够更深入地开发和利用Zemax软件。 Zemax编程语言(ZPL)是一种专门为光学设计软件Zemax设计的脚本语言。它允许用户编写...
ST语言,全称为Structured Text,是IEC 61131-3标准下的编程语言之一,常用于工业控制领域的PLC(可编程逻辑控制器)编程。ST语言以其丰富的表达能力和接近高级编程语言的语法特性,深受工程师们的青睐。本手册全面...
汇编语言是一种面向机器的编程语言,它可以直接利用计算机硬件特性并能通过汇编指令直接控制机器硬件。因此,使用汇编语言可以编写出在时间和空间上效率最优的程序。然而,汇编语言的学习并不是一件容易的事,它需要...
Plant Simulation编程语言SimTalk 2.0官方说明 Plant Simulation编程语言SimTalk 2.0是Tecnomatix ...SimTalk 2.0语言是Plant Simulation软件中的一种强大且灵活的编程语言,可以帮助用户更好地模拟和控制模拟模型。
Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 Rust 编程语言入门 ...
编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集大全 ,学习进步阶梯必备。编程语言Python算法集...
如果编程语言里面的所有英语单词你都不认识,那么很难想像你怎么可能学会编程语言。 特别是当看别人的源码或自己写源码时,会用到很多的英语单词来命名变量名称、类名称、字段名称等的,如果单词量达不到的话,会...
在IT领域,编程语言是构建软件、应用和服务...无论是JDBC这样的数据库编程技术,还是对各种编程语言的深入学习,都能增强开发者解决问题的能力,适应不断变化的技术需求。持续学习和实践是IT行业保持竞争力的关键所在。
ST语言,全称为Structured Text,是一种结构化文本编程语言,被广泛应用于工业自动化领域的PLC(可编程逻辑控制器)编程。该语言完全符合国际电工委员会(IEC)制定的标准IEC61131-3,是PLCopen组织推荐的五种标准...
通过与这些编程语言创始人的对话,我们可以从他们的口中得知为什么他们认为有必要创建新的编程语言,它们的技术开发细节,以及如何教授和学习这些语言。书中也探讨了编程语言如何随着时间的推移而发展,以及如何适应...
ST语言,也称为结构化文本(ST),是一种高级编程语言,它基于文本,用于工业自动化和控制系统的编程。根据IEC61131-3标准,ST语言被广泛应用于可编程逻辑控制器(PLC)和其他工业设备中。PLCopen是一个全球性的行业协会...