如何用函数表示数(二)引入闭包
2013-07-14 09:01
337 查看
在现实的编码中,我们更多的遇到的是像这样的式子
g(f(x)+y,z)或f(x,y,z)之类
因此如何表示多目成了一个问题。
你可以说用f(x,y)表示x,y,在多目调用的地方则改写成g(f(x,y))。
且不说,就算能把g(x,f)改写成g(x),那么f(x,y)又用什么方法来改写呢?这就成了一个无头无尾的问题。
为了消除在讨论过程中多元运算的影响,一个叫“闭包”的工具被引入了进来。闭包的作用,就是能将多目运算简化为一元运算。看个例子。
平时我们写加法运算是这样写的。
我们还可以用闭包,将加法这个二元运算改为1元运算
分解为
看add2的源码,add2返回的并不是一个数,而是一个匿名的函数。这个函数干什么呢?当传入4时,就返回3+4,当传入5时,就返回3+5。
这样我们就把二元函数改为了一元函数。
只学过Java的童鞋在这里一定会想到了,“嗯,我可以构建一个加法器,完成的事情是一样的。”
正是如此,在传统的Java中可以这么写
1.在现下支持闭包特性的语言,基本上都是通过类似类的结构来实现闭包的。
2.闭包是事实上的匿名类,临时类,而闭包在绝大多数时候都是当匿名类,临时类在使用的。
而闭包和类的最大共同点,就是他们能够在其内部保持状态值。
可以这么说,如果不需要保持中间状态值,就无使用闭包或类的必要。只学过Java的童鞋肯定不会认同这句话,他们会在你耳边抗议说,类比闭包更有用,类有更fancier的特性,比如“继承”,“多态”等等……我承认,有些时候这些特性都很有用,但在一些时候,他们跟我们要解决的问题没有任何关系。
比如,在很多时候我要的是一个结果,我并不关心中间过程。我会这么写一个下载文件的方法
而如果用典型的面向对象的方法:
1.你需要定义一个封装下载器的对象。
2.再定义一个Event对象,来封装你需要传递的数据信息。
3.再定义一个Ilistener接口
4.在下载器上添加一个event listener用于监听下载是否完成。
5.当listener监听到了下载完毕后,再将下载的内容封装到步骤2自定义的Event中,再dispatch这个event出去
6.在调用的地方,你先要实现一个步骤3中listener的接口
7.new一个步骤1中封装器的对象
8.再在这个对象上添加event listener,把实现listener接口的对象监听进去(你使用的是标准的接口吗?不是的话还得自定义一个IDispather!,好吧,你又在庆幸你之前已经继承了某个遥远的类,这类已经前瞻性的把这些接口都实现了,而当你还在为你的OO实现而自鸣得意时,这时需要一个新的接口而你的父类又不被允许实现这个接口时,你立刻傻缺了)
9.当listener接口的方法被调用时,你终于可以得到想要的东西了…………
记得学Java时,我和我的同学无一不被其超大的代码量,超繁琐的过程给震慑,以至于我们忙活了半天,也不明白我们究竟在干什么,但我们没有对此表示过怀疑,以为编程就必须这样繁琐,因为老师告诉我们,Java是世界上最好的语言,学会Java你就不需要学其他语言了……过去的血泪就别再提了……终于有一天,你忍不住了,写了个静态方法把东西都扔到里面去了事。因为最终你发现,你需要的只是通过一个回传函数,把数据取出来而已。
所以我相信,即使是最忠实的OO信徒也不会反对这个观点:把问题抽象到更高的层次去解决,[b]更能反映出问题的本质,并为解决问题提供更大的灵活性。[/b]
如果总是停留在较低的层面上,就容易耽搁于细节而忘了最初的目的。
闭包恰好就是对类和结构的更高层次的抽象。扯得有点远了,下一章我们再回到主题上来。
ps:听说新版的Java和C++11都要支持闭包了,经过20年之后,他们终于意识到了!
(待续)
g(f(x)+y,z)或f(x,y,z)之类
因此如何表示多目成了一个问题。
你可以说用f(x,y)表示x,y,在多目调用的地方则改写成g(f(x,y))。
且不说,就算能把g(x,f)改写成g(x),那么f(x,y)又用什么方法来改写呢?这就成了一个无头无尾的问题。
为了消除在讨论过程中多元运算的影响,一个叫“闭包”的工具被引入了进来。闭包的作用,就是能将多目运算简化为一元运算。看个例子。
平时我们写加法运算是这样写的。
var i = 3+4;我们也可以定义个函数来完成加法。
function add(a,b){return a+b;} print(add(3,4));这看上去有点傻。但它的结果是一样的。
我们还可以用闭包,将加法这个二元运算改为1元运算
function add2(a){ return function(b){ return a+b; } } print(add2(3)(4));对闭包不熟悉的童鞋,这里稍微做点讲解。
print(add2(3)(4));
分解为
f = add2(3); print(f(4));
看add2的源码,add2返回的并不是一个数,而是一个匿名的函数。这个函数干什么呢?当传入4时,就返回3+4,当传入5时,就返回3+5。
这样我们就把二元函数改为了一元函数。
只学过Java的童鞋在这里一定会想到了,“嗯,我可以构建一个加法器,完成的事情是一样的。”
正是如此,在传统的Java中可以这么写
class adder = new Adder(3); System.out.println(adder.add(4)); System.out.println(adder.add(5));这道出了关于闭包的两个事实:
1.在现下支持闭包特性的语言,基本上都是通过类似类的结构来实现闭包的。
2.闭包是事实上的匿名类,临时类,而闭包在绝大多数时候都是当匿名类,临时类在使用的。
而闭包和类的最大共同点,就是他们能够在其内部保持状态值。
可以这么说,如果不需要保持中间状态值,就无使用闭包或类的必要。只学过Java的童鞋肯定不会认同这句话,他们会在你耳边抗议说,类比闭包更有用,类有更fancier的特性,比如“继承”,“多态”等等……我承认,有些时候这些特性都很有用,但在一些时候,他们跟我们要解决的问题没有任何关系。
比如,在很多时候我要的是一个结果,我并不关心中间过程。我会这么写一个下载文件的方法
function loadJSONDoc(url,callback) { var xmlhttp=new XMLHttpRequest(); if(callback){ xmlhttp.onreadystatechange=function(){ if (xmlhttp.readyState==4 && xmlhttp.status==200){ callback(JSON.parse(xmlhttp.responseText)); } }; } xmlhttp.open("POST",'/'+url,true); xmlhttp.send(); }
而如果用典型的面向对象的方法:
1.你需要定义一个封装下载器的对象。
2.再定义一个Event对象,来封装你需要传递的数据信息。
3.再定义一个Ilistener接口
4.在下载器上添加一个event listener用于监听下载是否完成。
5.当listener监听到了下载完毕后,再将下载的内容封装到步骤2自定义的Event中,再dispatch这个event出去
6.在调用的地方,你先要实现一个步骤3中listener的接口
7.new一个步骤1中封装器的对象
8.再在这个对象上添加event listener,把实现listener接口的对象监听进去(你使用的是标准的接口吗?不是的话还得自定义一个IDispather!,好吧,你又在庆幸你之前已经继承了某个遥远的类,这类已经前瞻性的把这些接口都实现了,而当你还在为你的OO实现而自鸣得意时,这时需要一个新的接口而你的父类又不被允许实现这个接口时,你立刻傻缺了)
9.当listener接口的方法被调用时,你终于可以得到想要的东西了…………
记得学Java时,我和我的同学无一不被其超大的代码量,超繁琐的过程给震慑,以至于我们忙活了半天,也不明白我们究竟在干什么,但我们没有对此表示过怀疑,以为编程就必须这样繁琐,因为老师告诉我们,Java是世界上最好的语言,学会Java你就不需要学其他语言了……过去的血泪就别再提了……终于有一天,你忍不住了,写了个静态方法把东西都扔到里面去了事。因为最终你发现,你需要的只是通过一个回传函数,把数据取出来而已。
所以我相信,即使是最忠实的OO信徒也不会反对这个观点:把问题抽象到更高的层次去解决,[b]更能反映出问题的本质,并为解决问题提供更大的灵活性。[/b]
如果总是停留在较低的层面上,就容易耽搁于细节而忘了最初的目的。
闭包恰好就是对类和结构的更高层次的抽象。扯得有点远了,下一章我们再回到主题上来。
ps:听说新版的Java和C++11都要支持闭包了,经过20年之后,他们终于意识到了!
(待续)
相关文章推荐
- 如何解决闭包只能取得包含函数中任何变量的最后一个值
- 如何解决闭包只能取得包含函数中任何变量的最后一个值
- EF学习杂记31:如何在EF查询中引入外部函数
- laravel 如何引入自己的函数或类库
- laravel 如何引入自己的函数或类库
- 忆龙2009:C# 如何取得取得实例类名称的字符串表示 、当前运行的函数的名称
- 如何用函数表示数(三)数的消失
- 忆龙2009:C# 如何取得取得实例类名称的字符串表示 、当前运行的函数的名称
- C++如何在类的成员函数中表示同名的全局变量?
- 如何用函数表示数 (一)
- 如何用函数表示数(五)其他运算
- swift 之 如何在函数中把闭包作为参数 进行回调
- 如何 才能把函数公式表示出来呢?如f(t)=3t+2t*t+4t*t*t,并能够调用这个。
- 如何编写管道函数
- swift基础学习(04)[闭包、函数、枚举、类和结构体]
- C++ 函数中如何接收数量不定的函数参数
- javascript SpiderMonkey中的函数序列化如何进行
- 【面向对象程序设计常见面试题】如何定义和实现一个类的成员函数为回调函数?
- C语言中的负数是如何表示的?