软件随想录:程序员部落酋长Joel谈软件(local.joelonsoftware.com/wiki)-22

来源:互联网 发布:喀秋莎软件下载 编辑:程序博客网 时间:2024/05/02 19:29

你的程序语言可以这样做吗?

From The Joel on Software Translation Project

Jump to: navigation, search

你的编程语言可以这样做吗?

有一天,你在浏览自己的程序代码,发现有两大段程序代码几乎一样。实际上它们的确一样,除了一个关于「Spaghetti」而另一个关于「Chocolate Moose」。

   // A trivial example:      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");

01BorkBorkBork.PNG

嗯,这个例子很经典,但你能想到一个更深入的例子。这段程序代码的优勝之处有很多,你听过上千次的:可维护性、可读性、抽象性 = 好!

现在你留意到有另外两段程序代码一模一样,除了一个反覆呼叫一个叫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要求你建立一个有单method的对象,称之为functor。另外许多面向对象语言要求你为每件class都建立一个文件,结果变得不怎么快(klunky fast)。如果你的编程语言要求使用functor,就不能彻底得到现代编程环境的好处。看看你可否退货拿回些钱。

不过写出那些仅仅只是对数组中每个元素做事的小小函数,究竟能得到多少好处嘱?

让我们回到'map'函数。对数组內的每个元素做事时,很可能并不在乎哪个元素先做。无论由第一个还是最后一个元素开始,结果都是一样的,对不对?如果你手头上有2个CPU,就可以写段程序代码,使得它们各对一半的元素工作,于是'map'就变快两倍了。

或者你在全球有千千百百台服务器(只是假设),还有一个很大很大的数组,存放整个互联网的內容(同样也只是假设)。现在你可以在这些服务器上执行'map',让各台服务器只处理问题很小的一部份。

所以现在可以再举个例子,要写出能超快速搜寻整个互联网的程序代码其实很简单,只要呼叫一个以基本字串搜寻器作为参数的'map'函数即可。

这里头有件真正有趣的事值得注意:当你把'map'和'reduce'想成每个人都能用的函数,而大家也都在用,只要有个超级天才,写出能在全球巨型平行电脑阵列上执行'map'和'reduce'的程序代码,那么所有原本用单一循环能正常运行的旧程序代码,还是照样能用,但是却会快上千万倍,也就是说可以用来在瞬间解決掉巨大的问题。

Lemme重覆了这一点。它把循环的基本概念抽象出来,你可以用任何所要的方式实现循环,其中包括能适切地配合额外硬件的实现方式。

你现在明白我之前写到不满那些除了Java之外甚么都没被教过的电脑科学学生:

不了解functional programming就无法发明MapReduce这个让Google延展性如此强大的演算法。Map和Reduce这个术语源自Lisp和functional programming。回想起来,对还记得6.001或等同程序课的人来说MapReduce实在是很明显的事情,纯粹的functional programs 没有副作用,所以能轻易地平行化。

Google发明了MapReduce而微软没有,这个事实在某方面解释以下的现况:当微软还在努力让基本搜寻功能会动时,Google已经进入下一个问题,建立Skynet这个世界上最大的大规模平行运算超级电脑。我不认为微软真的了解他们在这一波风潮落后了多少。

我希望你现在明白,有第一级函数的编程语言让你找到更多抽象化的机会,也就是说你程序代码会更小、更紧密、更便于再用而且延展性更佳。无数的Google应用软件使用MapReduce,因此一有人改进其效率或修正臭虫,这些应用软件都得益了。

现在我有一点点激动了,我要说最有生产效益的编程环境,莫过于能让你在不同的抽象层次作业的环境。老掉牙的GW-BASIC不让你写函数。C有函数指针,但是丑陋之极又不许匿名,一定得在其他地方实现,不能直接写在使用的地方。Java则是让你使用functor这个更丑陋的东西。正如Steve Yegge所述,Java是个名词王国。


注:作者原文写了FORTRAN,他已作出更正启示。他对上一次使用FORTRAN是27年前,当时的FORTRAN也有函数。

这些网页的內容为表达个人意见。
All contents Copyright © 1999-2006 by Joel Spolsky. All Rights Reserved.

Retrieved from "http://local.joelonsoftware.com/wiki/%E4%BD%A0%E7%9A%84%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80%E5%8F%AF%E4%BB%A5%E9%80%99%E6%A8%A3%E5%81%9A%E5%97%8E%EF%BC%9F"
原创粉丝点击