函数式编程FP前言Javascript,有人称其为C+LISP,C只怕是尽人皆知
2010-06-01 14:28 软件IT 阅读(594) 评论(0) 编辑 收藏 举报函数式编程FP前言Javascript,有人称其为C+LISP,C只怕是尽人皆知,但是一直活跃在人工智能领域的另一个古老而优美的语言LISP,掌握的恐怕不是很多.这个倒不是因为这个语言太难或者用途不广泛,而是大多数人在接受计算机语言启蒙的时候都走的是图灵机模式,而LISP,做为一种函数式编程语言,是另一个体系:lambda演算体系.这个体系的运算能力跟图灵机的运算能力是相当的。所以Javascript本身是一种很自由的,支持函数式编程的一个神奇的语言,在WEB中的应用只是它的以个小小的部分。脚本可以用来脚本化很多东西,最主要的应用是在UI层面,很灵活(这也是Javascript用来脚本化HTML的一个重要原因)。我们这里要说的是一个100%java实现的javascript引擎rhino,当然重点不是引擎本身,而是在其上解释JavaScript的函数式编程。
重庆软件培训函数式编程概览我们先看几个例子,从感官上对其有一个了解,看一个幂计算函数,用命令式语言书写(命令式语言如C,Java等),大概就是下面这个样子:functionexpt(b,n){if(n==0){return1;}else{returnexpt(b,n-1)*b;//正常的递归调用}}再看看函数式编程的写法:functionexpt(b,n){if(n==0){return1;}else{returnmul(expt(b,dec(n)),b);//所有操作均为函数}}可以很明显的看到,有一大堆的括号,没有操作符(如+-*/等),这是因为,操作符在函数式编程中被认为是函数,与其他函数(数学函数,串处理函数等)的地位是同等的,当然这个不是最主要的,在函数式编程中最主要的是函数可以做为一个基本类型被返回,这一点时命令式语言无法完成的。比如,定义一个函数,输入两个参数,计算这两个数的平方和:function(x,y){ returnadd(expt(x,2),expt(y,2)); }你可以将这个函数赋值给一个变量,如下:var func = function(x,y){returnadd(expt(x,2),expt(y,2));}然后,最神奇的是,下边这样:func(3,4);//这个表达式将返回25! 此时的func已经是一个函数了!好了,简单的概述就此为止,下面我们看一些更高级的主题:高阶函数。
高阶函数事实上,所有的有关函数式编程的文章必须要涉及到这个主题,这是因为,在命令式语言中,我们的抽象是根据"类"(这正是现在流行的OO的基本思想)来进行的,但是,在函数式编程中,没有办法表示类的概念,但是同样可以进行高级的抽象方式,使得一个函数更加泛化,可以被"实例化"成其他的函数,这个就是高阶函数。来看个例子,重庆软件学院有这样几种求和运算:functionintSum(a,b){functioninc(x){returnx+1;}functionidentity(x){returnx;}if(a>b){return0;}else{returnintSum(inc(a),b)+identity(a);}}functioncubeSum(a,b){functioninc(x){returnx+1;}functioncube(x){returnx*x*x;}if(a>b){return0;}else{returncubeSum(inc(a),b)+cube(a);}}functionpiSum(a,b){functionpiTerm(x){return1/((x+2)*x);}functionpiNext(x){returnx+4;}if(a>b){return0;}else{returnpiSum(piNext(a),b)+piTerm(a);}}第一个函数用来计算从a-b的数的总和,步长为1,第二个函数计算a-b的立方和,步长为1,第三个函数计算a-b的一个方程的和(将a-b中的每一个数带入此方程进行计算),步长为4.从函数的形式以及函数的作用来看,这三个函数有很大的共性,所以我们考虑是否可以将这些共性抽取出来,将每次计算的步长和方程传入,进行求和计算??答案当然是肯定的,下面我们来抽象:定义下一个参与计算的数(通过步长函数的定义)定义求什么的和(函数体的定义)有了这两个函数,我们就可以计算任意的方程,指定区间的求和操作,将上述的两个函数做为参数传入,输出即为运算结果:functionsum(term,a,next,b){if(a>b){return0;}else{returnsum(term,next(a),next,b)+term(a);}}这个函数需要四个参数,一个是关于计算子的定义term,一个是步长函数next,另外两个即为区间的两个端点a,b,这样我们可以重新定义上述的三个函数如下:functionintSum(a,b){functioninc(x){returnx+1;}functionidentity(x){returnx;}returnsum(identity,a,inc,b);//调用通用的抽象接口}functioncubeSum(a,b){functioninc(x){returnx+1;}functioncube(x){returnx*x*x;}returnsum(cube,a,inc,b);//调用通用的抽象接口}functionpiSum(a,b){functionpiTerm(x){return1/((x+2)*x);}functionpiNext(x){returnx+4;}returnsum(piTerm,a,piNext,b);//调用通用的抽象接口}高阶函数提供了更高级的抽象,从而使得程序的结构更加清晰。
下面我们再看看函数式语言的优雅的代码,匿名函数:匿名函数我们先对一些简单的操作进行简单的包装(如四则运算,boolean运算等操作):functionabs(x){returnx>0?x:-x;}functionadd(a,b){returna+b;}functionsub(a,b){returna-b;}functionmul(a,b){returna*b;}functiondiv(a,b){returna/b;}functionrem(a,b){returna%b;}functioninc(x){returnx+1;}functiondec(x){returnx-1;}functionequal(a,b){returna==b;}functiongreat(a,b){returna>b;}functionless(a,b){returna<b;}functionnegative(x){returnx<0;}functionpositive(x){returnx>0;}然后,在这些共用的语法糖(并非严格意义上的语法糖,但是它们的确是!)的基础上,做一些简单的函数定义://n*(n-1)*(n-2)*...*3*2*1functionfactorial(n){if(equal(n,1)){return1;}else{returnmul(n,factorial(dec(n)));}}//对上边的函数的另一种定义方式/**product<-counter*product*counter<-counter+1**/functionfactorial(n){functionfact_iter(product,counter,max){if(counter>max){returnproduct;}else{fact_iter(mul(counter,product),inc(counter),max);}}returnfact_iter(1,1,n);}functionexpt(b,n){if(n==0){return1;}else{returnmul(expt(b,dec(n)),b);}}functiongcd(a,b){if(b==0){returna;}else{returngcd(b,rem(a,b));}}functionsearch(fx,neg,pos){functioncloseEnough(x,y){returnless(abs(sub(x,y)),0.001)};varmid=(function(x,y){return div( add(x, y), 2);})(neg,pos);if(closeEnough(neg,pos)){returnmid;}else{vartest=fx(mid);if(positive(test)){returnsearch(fx,neg,mid);}elseif(negative(test)){returnsearch(fx,mid,pos);}else{returnmid;}}}functionhalfIntervalMethod(fx,a,b){varav=fx(a);varbv=fx(b);if(negative(av)&&positive(bv)){returnsearch(fx,a,b);}elseif(negative(bv)&&positive(av)){returnsearch(fx,b,a);}else{print("errorhappend!!");}}//计算一个函数的不动点functionfixedPoint(fx,first){vartolerance=0.00001;functioncloseEnough(x,y){returnless(abs(sub(x,y)),tolerance)};functionTry(guess){varnext=fx(guess);//print(next+""+guess);if(closeEnough(guess,next)){returnnext;}else{returnTry(next);}};returnTry(first);}//magicfunctionsqrt,alittlehardtoread,hah?functionsqrt(x){returnfixedPoint(function(y){returnfunction(a,b){returndiv(add(a,b),2);}(y,div(x,y));},1.0);}如果上边的几个都可以完全理解,那么常识看看最下面的这个计算一个函数的平方根的函数sqrt(x),你会发现这个函数很有意思,其中的那个匿名函数最有意思,我写了个测试函数:functionfuncsTester(){ //计算两个数的平方和的匿名函数vary=(function(x,y){returnadd(expt(x,2),expt(y,2));})(3,4);print(y);print(halfIntervalMethod(sin,2.0,4.0));print(halfIntervalMethod(function(x){returnexpt(x,3)-mul(2,x)-3;},1.0,2.0));//x^3-2x-3print(fixedPoint(cos,1.0));//cos的不动点print(fixedPoint(function(x){returnadd(sin(x),cos(x));},1.0));print(sqrt(100));}http://baike.baidu.com/view/3292182.html
运行结果如下:js>funcsTester()253.141113281251.893066406250.73908229852240241.258731596297117310js>好了,关于函数式编程就大概介绍到这里,下面简单说说这个rhino包关于Rhinorhino是一个纯java的javascript引擎的实现,下载之后,将js.jar加入classpath,然后在命令行中输入:java org.mozilla.javascript.tools.shell.Main即可启动,可以使用load(path/of/script)来加载,加载完成后即可使用脚本中定义的函数,非常方便,当然DOM中的一切是不能用的,比如alert什么的,但是javascript不只是用来脚本化WEB页面的。可以使用print进行打印,使用quit()退出等,下面做一个关于rhino的例子:user={name:"abruzzi",password:"123456",address:{zip:"612345",street:"westHuangQuanroad"},getName:function(){returnthis.name;},getPassword:function(){returnthis.password;},getAddress:function(){returnthis.address.zip+"\n"+this.address.street;}}$java org.mozilla.javascript.tools.shell.MainRhino1.7release220090322js>load('~/development/myLib/HighOrderFunc/json.js')js>user[objectObject]js>user.getName()abruzzijs>user.getPassword()123456js>user.getAddress()612345westHuangQuanroadjs>在rhino的发行包中有一些特别有趣的例子,大家不妨自己动手做一下,看看效果,同时体会一下函数式编程的优美,简介。足下软件学院关注