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


Posted in Javascript onSeptember 07, 2006

有一天,你在浏览自己的代码,发现有两大段代码几乎一样。实际上,它们确实是一样的——除了一个关于意大利面(Spaghetti)而另一个关于巧克力慕思(Chocolate Moose)。 

  // 一个小例子:

  alert("偶要吃意大利面!");
  alert("偶要吃巧克力慕思!");
嗯,这个例子碰巧是用javascript写的,不过你就算不懂JavaScript,应该也能明白它在干什么。 

拷贝代码不好。于是,你创建了个函数 

  function SwedishChef( food ){
      alert("偶要吃" + food + "!");
  }
  SwedishChef("意大利面");
  SwedishChef("巧克力慕思");
Ok,这只是一个很小很小的例子而已,相信你能想像到个更实际一点的例子。这段代码有很多优点,你全都听过几万次了:可维护性、可读性、抽象性 = 好! 

现在你留意到有另外两段代码几乎跟它们一模一样,除了一个反复调用一个叫BoomBoom的函数,另一个反复调用一个叫PutInPot的。除此之外,?两段代码简直没什么两样: 

  alert("拿龙虾");
  PutInPot("龙虾");
  PutInPot("水");
  alert("拿鸡肉");
  BoomBoom("鸡肉");
  BoomBoom("椰子酱");
现在要想个办法,使得你可以?⒁桓龊??米髁硪桓龊??牟问?U馐歉鲋匾?哪芰Γ?蛭?愀?菀捉?蚣艽?胄闯梢桓龊???mu注:还记得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长得很像,你也许想把它们抽象为一个将数组内的所有元素按某种算法汇总起?淼姆盒秃???nbsp;

  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有函数指针,但你要在?e处声明和定义函数)。面向对象语言也不确保你用函数可以干些啥(把函数当对象处理?)。 

如果你想将函数视为一类对象,Java要求你建立一个有单方法的对象,称为算子对象。许多面向对象语言要你为每个类都建立一个完整文件,像这样开发可真叫快。如果你的编程?言要你使用算子对象来包装方法(而不是把方法本身当成对象),你就不能?氐椎玫较执?ǘ??┍喑逃镅缘暮么Α2环潦允钥茨憧煞裢嘶跄没匦┣??nbsp;

不用再写那些除了经过一个数组对每个元素做一些事情之外一无是处的函数,有什么好处? 

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

或者,发挥一下想像力,设想你在全球有千千万万台服务器分布在全世界的若干个数据中心,你有一个真的很大很大的数组,嗯,再发挥一下想像力,设想这个数组记录有整个互联网的内容。还了,现在你可以在几千台服务器上同时执行map,让每台服务器都来解决同一个问题的一小部分。 

那么在这个例子里面,编写一段非常快的代码来搜索整个互联网这个问题,其实就和用一个简单的字符串搜索器(算子)作为参数来调用map函数一样简单了。 

希望你注意到一个真正有意思的要点,如果你想要把map/reduce模式变成一个对所有人都有用,对所有人都能立刻派上用场的技术,你只需要一个超级天才来写最重要的一部分代码,来让map/reduce可以在一个巨大的并行计算机阵列上运行,然后其他旧的但是一向在单一个循环中运行良好的代码,仍可以保持正确的运行,惟一的差别只是比原来单机运行快了n倍。这意味着它们都一不留神突然变成可以被用来解决一个巨大的问题的代码。 

让我再??乱幌拢?ü?选把?贰闭飧龈拍罴右猿橄螅?憧梢园延萌魏文阆不兜姆绞嚼词迪帧把?贰惫?蹋??ǹ梢允迪秩醚?返??俣人孀庞布?扑隳芰Ρ3至钊寺?獾耐?皆龀ぁ?nbsp;

你现在应该可以明白不久为何对那些对除了Java之外什么都?]被学过的计算机系学生表示不满了:( http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html) :

Without understanding functional programming, you can't invent MapReduce, the algorithm that makes Google so massively scalable. The terms Map and Reduce come from Lisp and functional programming. MapReduce is, in retrospect, obvious to anyone who remembers from their 6.001-equivalent programming class that purely functional programs have no side effects and are thus trivially parallelizable. The very fact that Google invented MapReduce, and Microsoft didn't, says something about why Microsoft is still playing catch up trying to get basic search features to work, while Google has moved on to the next problem: building Skynet^H^H^H^H^H^H the world's largest massively parallel supercomputer. I don't think Microsoft completely understands just how far behind they are on that wave. 

不理解函数式编程,你就发明不了MapReduce这个让Google的计算能力如此具有可扩展性的算法。Map和Reduce这两个术语源自Lisp语言和函数式编程……(这是另一篇文章的内容,emu也不是很理解其中的各种说法的来龙去脉,就不翻译了) 

我希望你现在明白,把函数当成基本类型的(动态)编程语言能让你在编程过程中更好的进行抽象化,也就是使代码精悍、功能更内聚、更具可重用性及更具有扩展性。很多的Google应用使用Map/Reduce模式,因此一有人对其优化或修正缺陷,它们就都可以从中得益。 

我准备要再罗嗦一下,我认为最有生产力的编程语言莫过于能让你在不同层次上都可以进行抽象化的。老掉牙的FORTRAN 语言以前是不让你写函数的注。C 有函数指针,可是它们都非常丑丑丑丑丑丑丑丑陋,不允许匿名声明,又不能在用它们时实现它们而偏偏要放在?e处去实现。Java让你使用算子对象,一种更丑陋的东西。正如Steve Yegge所述,Java是??名词王国 (http://steveyegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)。 

作者注:这里提起了FORTRAN,不过我上次使用FORTRAN是27年前的事了。FORTRAN是有函数的,我码字那会儿脑子里面想的大概是GW-BASIC语言。(emu注,basic确实只有所谓的子程序和go-sub语句,作用只是重新组织代码结构而已,没有参数和调用堆栈,因此没有真正的函数调用)

译者注:原作者起了《你的编程语言可以这样做吗》这个标题其实并不是这篇文章的真正价值所在,我转这篇文章也不是因为原作者可以把语言的初级技巧玩得转,而是因为这是一篇map/reduce模型的示范。

Javascript 相关文章推荐
JScript 脚本实现文件下载 一般用于下载木马
Oct 29 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
Dec 28 Javascript
让你的博客飘雪花超出屏幕依然看得见
Jan 04 Javascript
jquery的选择器的使用技巧之如何选择input框
Sep 22 Javascript
jQuery切换所有复选框选中状态的方法
Jul 02 Javascript
JavaScript iframe数据共享接口实现方法
Jan 06 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
May 10 Javascript
几种二级联动案例(jQuery\Array\Ajax php)
Aug 13 Javascript
element上传组件循环引用及简单时间倒计时的实现
Oct 01 Javascript
angularJs select绑定的model取不到值的解决方法
Oct 08 Javascript
javascript for循环性能测试示例
Aug 07 Javascript
React中Ref 的使用方法详解
Apr 28 Javascript
音乐播放用的的几个函数
Sep 07 #Javascript
在网页中屏蔽快捷键
Sep 06 #Javascript
js的event详解。
Sep 06 #Javascript
农历与西历对照
Sep 06 #Javascript
MSN消息提示类
Sep 05 #Javascript
经验几则 推荐
Sep 05 #Javascript
JS实现浏览器菜单命令
Sep 05 #Javascript
You might like
如何做到多笔资料的同步
2006/10/09 PHP
php生成圆角图片的方法
2015/04/07 PHP
PHP Ajax实现无刷新附件上传
2016/08/17 PHP
php将服务端的文件读出来显示在web页面实例
2016/10/31 PHP
解决Laravel blade模板转义html标签的问题
2019/09/03 PHP
PHP使用JpGraph绘制折线图操作示例【附源码下载】
2019/10/18 PHP
不用写JS也能使用EXTJS视频演示
2008/12/29 Javascript
JavaScript 密码强度判断代码
2009/09/05 Javascript
jquery改变disabled的boolean状态的三种方法
2013/12/13 Javascript
ext combobox动态加载数据库数据(附前后台)
2014/06/17 Javascript
JS+CSS实现带小三角指引的滑动门效果
2015/09/22 Javascript
EditPlus 正则表达式 实战(3)
2016/12/15 Javascript
ES6概念 ymbol.for()方法
2016/12/25 Javascript
微信小程序实现页面跳转传递参数(实体,对象)
2019/08/12 Javascript
vuex根据不同的用户权限展示不同的路由列表功能
2019/09/20 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
JS如何在不同平台实现多语言方式
2020/07/16 Javascript
uni-app使用countdown插件实现倒计时
2020/11/01 Javascript
Django原生sql也能使用Paginator分页的示例代码
2017/11/15 Python
使用Python的package机制如何简化utils包设计详解
2017/12/11 Python
django Serializer序列化使用方法详解
2018/10/16 Python
Python连接SQLite数据库并进行增册改查操作方法详解
2020/02/18 Python
Python 字符串处理特殊空格\xc2\xa0\t\n Non-breaking space
2020/02/23 Python
python变量的作用域是什么
2020/05/26 Python
Keras中 ImageDataGenerator函数的参数用法
2020/07/03 Python
如何基于Python Matplotlib实现网格动画
2020/07/20 Python
详解Python中的路径问题
2020/09/02 Python
python统计mysql数据量变化并调用接口告警的示例代码
2020/09/21 Python
美国祛痘、抗衰老药妆品牌:Murad
2016/08/27 全球购物
技术总监的工作职责
2013/11/13 职场文书
个人能力自我鉴赏
2014/01/25 职场文书
我的画教学反思
2014/04/28 职场文书
药剂专业个人求职信范文
2014/04/29 职场文书
总结一些Java常用的加密算法
2021/06/11 Java/Android
Mybatis-Plus进阶分页与乐观锁插件及通用枚举和多数据源详解
2022/03/21 Java/Android
Python OpenCV超详细讲解基本功能
2022/04/02 Python