一日,你查看你的程序代码,你有两大块代码看起来几乎完全的一样。事实上它们就是完全一样,除了一个代码里说的是“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(Visual Basic),这些都是非常常见的编程语言,各自拥有独特的...
编程语言是计算机世界中的基础工具,它使得人们能够与机器沟通,实现各种复杂的功能。创建自己的编程语言,虽然是一项艰巨的任务...通过实践,你不仅能创造出属于自己的编程语言,还能在这个过程中成为更优秀的程序员。
"自制编程语言相关资料" 本资源摘要信息是关于自制编程语言的相关资料,涵盖了编程语言的设计、实现和使用等方面。下面是从给定的文件中提取的知识点: 编程语言设计 * 编程语言的设计是指根据一定的规则和规范...
根据提供的标题“编程语言原理(第10版)”及描述“编程语言原理(第10版 编程语言原理)”,我们可以推断出这本书主要讲述了编程语言的基础理论与实践应用,是学习和理解编程语言设计与实现的重要参考资料。...
标题中的“编程语言代码转换器”指的是一个工具或软件,其主要功能是将一种编程语言的源代码自动转换为另一种编程语言的等效代码。这种转换器在多种场景下非常有用,例如,当开发者需要将现有项目从一种语言迁移至另...
ABB PLC ST语言编程手册是ABB公司为用户提供的关于Structured Text(ST)编程语言的详细指南。ST语言是一种基于ISO 11385标准的高级编程语言,常用于工业自动化领域的PLC(可编程逻辑控制器)编程。这篇手册旨在帮助...
Plant Simulation编程语言SimTalk 2.0官方说明 Plant Simulation编程语言SimTalk 2.0是Tecnomatix Plant Simulation软件中的一种编程语言,用于扩展模拟模型的功能和控制。SimTalk语言可以与物流对象和信息流对象...
ST是一种基于高级编程语言的编程方法,类似于C或 Pascal,适合于处理复杂的逻辑和数学运算。它提供了丰富的控制结构,如循环、条件语句和函数定义,使程序员能更清晰地表达逻辑流程。ST的灵活性和可读性是其主要优点...
### 编程语言发展历史概览 编程语言作为计算机科学的核心组成部分之一,其发展历程见证了信息技术的巨大变革。本文将从20世纪50年代之前的机器语言和汇编语言说起,逐步展开对主流编程语言的起源、演变及其相互之间...
在实验过程中,你需要熟练掌握编程语言的特性,理解底层工作机制,同时熟悉性能分析工具,如 profiling 和 benchmarking,以量化优化效果。此外,理解和利用编译器优化标志,如 gcc 或 clang 的-O选项,以及了解如何...
西门子PLC的几种编程语言 西门子PLC的编程语言是PLC技术的核心组件之一,它们之间的异同是PLC开发者需要掌握的基本知识。西门子PLC提供了多种编程语言,满足不同的应用需求和开发人员的编程习惯。 1. 顺序功能图...
标题与描述中的“fanuc梯形图编程语言说明书”指向的是FANUC PMC(Programmable Machine Controller)的编程指南,特别关注了梯形图这一编程语言。FANUC是全球领先的数控系统制造商,其PMC系统广泛应用于各种工业...
阅读"编程语言实现模式.pdf"这样的资料,可以帮助开发者更好地理解和掌握如何在实际项目中应用这些模式,从而提升软件开发的效率和质量。同时,熟悉多种语言的模式实现也有助于成为一名跨语言的全能开发者,能够更好...
计算机也一样,当你想让计算机为你做一件事情的时候,计算机本身并不能主动为我们工作,因此我们必须对它下达指令,而它根本不会也不可能听懂人类自然语言对事情的描述,因此我们必须使用程序来告诉计算机做什么事情...
注解是JAVA编程语言中的一种特殊语法元素,它能帮助程序员在软件开发过程中合理使用JAVA语言,从而开发出更加安全、高效、稳定的软件产品。 为了确保软件开发的安全性,本文提出了一系列的技术和方法,包括: - 在...
LET 模型的时间语义编程语言 LET 模型的时间语义编程语言是近年来实时系统领域的研究热点之一。实时系统的正确性不仅取决于逻辑正确性,还取决于时间正确性。然而,现有的编程语言缺乏显示表达系统时间属性的语法...
通过学习和实践自制编程语言,不仅可以深化对计算机科学原理的理解,还能提升解决问题的能力,为成为一名优秀的软件开发者奠定坚实基础。对于那些热爱编程和对计算机工作原理感兴趣的人来说,这是一个非常有价值的...
在计算机科学领域,汇编语言是一种低级编程语言,它与机器语言紧密相关,每个指令对应一个特定的机器码。尽管汇编语言的直接操作性和效率很高,但它的语法通常较为复杂,对初学者来说可能不太友好。为了简化这一过程...