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


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 相关文章推荐
页面刷新时记住滚动条的位置jquery代码
Jun 17 Javascript
对JavaScript的全文搜索实现相关度评分的功能的方法
Jun 24 Javascript
js内置对象处理_打印学生成绩单的简单实现
Sep 24 Javascript
100多个基础常用JS函数和语法集合大全
Feb 16 Javascript
bootstrap如何让dropdown menu按钮式下拉框长度一致
Apr 10 Javascript
vuex提交state&amp;&amp;实时监听state数据的改变方法
Sep 16 Javascript
Vue的H5页面唤起支付宝支付功能
Apr 18 Javascript
javascript异步编程的六种方式总结
May 17 Javascript
微信小程序 wxParse插件显示视频问题
Sep 27 Javascript
layui+jquery支持IE8的表格分页方法
Sep 28 jQuery
js中apply和call的理解与使用方法
Nov 27 Javascript
JavaScript设计模式之观察者模式与发布订阅模式详解
May 07 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
windows下配置apache+php+mysql时出现问题的处理方法
2014/06/20 PHP
PHP查找与搜索数组元素方法总结
2015/06/12 PHP
php读取txt文件并将数据插入到数据库
2016/02/23 PHP
php简单实现sql防注入的方法
2016/04/22 PHP
基于Laravel实现的用户动态模块开发
2017/09/21 PHP
IE6/7 and IE8/9/10(IE7模式)依次隐藏具有absolute或relative的父元素和子元素后再显示父元素
2011/07/31 Javascript
div+css+js实现无缝滚动类似marquee无缝滚动兼容firefox
2013/08/29 Javascript
JS操作iframe里的dom(实例讲解)
2014/01/29 Javascript
Webpack 实现 Node.js 代码热替换
2015/10/22 Javascript
Angular.js初始化之ng-app的自动绑定与手动绑定详解
2017/07/31 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
seajs实现强制刷新本地缓存的方法分析
2017/10/16 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
2018/08/13 Javascript
基于Vue 服务端Cookies删除的问题
2018/09/21 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
Vue数据双向绑定底层实现原理
2019/11/22 Javascript
elementUI同一页面展示多个Dialog的实现
2020/11/19 Javascript
[01:21]DOTA2新纪元-7.0新版本即将开启!
2016/12/11 DOTA
Python ZipFile模块详解
2013/11/01 Python
python开发之函数定义实例分析
2015/11/12 Python
利用Python如何生成随机密码
2016/04/20 Python
python模块之sys模块和序列化模块(实例讲解)
2017/09/13 Python
Python2和3字符编码的区别知识点整理
2019/08/08 Python
windows、linux下打包Python3程序详细方法
2020/03/17 Python
美国家庭鞋店:Shoe Sensation
2019/09/27 全球购物
班级入场式解说词
2014/02/01 职场文书
策划创业计划书
2014/02/06 职场文书
幼儿园毕业寄语
2014/04/03 职场文书
家长会学生演讲稿
2014/04/26 职场文书
大学军训自我鉴定大全
2014/09/18 职场文书
毕业论文答辩演讲稿
2015/06/23 职场文书
单位车辆管理制度
2015/08/05 职场文书
AJAX实现指定部分页面刷新效果
2021/10/16 Javascript
NGINX 权限控制文件预览和下载的实现原理
2022/01/18 Servers
Python实现归一化算法详情
2022/03/18 Python
在容器中使用nginx搭建上传下载服务器
2022/05/11 Servers