比如,我们每次需要计算一个数的立方数时,都需要写成x*x*x的形式,如果一个运算中有很多地方要用到立方这种运算的话,可能编码会非常麻烦,而且代码也不清晰,于是,我们可以将立方运算抽象成一个函数,这个函数接受一个输入,并返回这个输入的立方数。
比如:
- function cube(x){return x*x*x;}
function cube(x){return x*x*x;}
这样,我们就有了一个独立的模块,用以计算“立方”,并且通过扩展语言的原生语句,我们可以表达“立方”这个概念。这种抽象是容易理解的,像C,java等语言也提供此类的抽象。
下面再看一个例子:
我们先编写一个函数,这个函数的功能如下:
- 输入一个范围[a,b]
- 输出为这个范围中的数的和。
如输入[1,100],输出5050.
这个函数通过递归的形式比较容易实现,如:
- function intSum(a, b){
-
function inc(x){ return x + 1; }
-
function identity(x){ return x; }
-
-
if(a > b){
-
return 0;
-
}else{
-
return intSum( inc(a) , b) + identity(a);
- }
- }
function intSum(a, b){
function inc(x){ return x + 1; }
function identity(x){ return x; }
if(a > b){
return 0;
}else{
return intSum( inc(a) , b) + identity(a);
}
}
当然,其中的内部函数inc,identity实在太简单,完全可以忽略,但是考虑到后边要用到,就先写成这种形式。
这个函数可以很好的工作,但是,随着项目的增大,我们需要另外一个函数来计算某个范围内数的立方和。
- 输入一个范围[a,b]
- 输出为这个范围中的数的立方和
如输入[1,4],输出100:1^3+2^3+3^3+4^3 = 1 + 8 + 27 + 64 = 100
这时候,我们同样可以用一个递归来实现这个函数:
- function cubeSum(a, b){
-
function inc(x){ return x + 1; }
-
function cube(x){ return x * x * x; }
-
if(a > b){
-
return 0;
-
}else{
-
return cubeSum( inc(a) , b) + cube(a);
- }
- }
function cubeSum(a, b){
function inc(x){ return x + 1; }
function cube(x){ return x * x * x; }
if(a > b){
return 0;
}else{
return cubeSum( inc(a) , b) + cube(a);
}
}
随着项目的进一步增长,我们现在需要实现第三个函数,来对这样一个数列进行求和输入同样为[a,b]的一个范围,计算1/x*(x+2),且步长为4,这个数列的前n项的和有个性质,就是无限的接近pi/8这个数列当然是有用的,比如计算Pi值,我们给定一个比较大的范围如[1,10000]然后给这个值乘8,即可近似的计算出pi来。
我用mathematica 计算了一下,可以看出这个求和确实是极限于PI/8.
同样用递归可以实现这个函数:
- function piSum(a, b){
-
function piTerm(x){ return 1/((x+2)*x); }
-
function piNext(x){ return x+4; }
-
-
if(a > b){
-
return 0;
-
}else{
-
return piSum( piNext(a) , b) + piTerm(a);
- }
- }
function piSum(a, b){
function piTerm(x){ return 1/((x+2)*x); }
function piNext(x){ return x+4; }
if(a > b){
return 0;
}else{
return piSum( piNext(a) , b) + piTerm(a);
}
}
现在,让我们比较一下这三个函数,很快我们就会发现,这三个函数太相似了,都是:
- 比较范围的下标,如果a>b,返回0,否则,转2
- 改变a的值,并对a作一定的运算,然后递归调用自身
可以写成下面的这种伪码形式:
- function <funcName>(a, b){
-
if(a > b){
-
return 0;
-
}else{
-
return <funcName>(<next>(a), b) + <func>(a);
- }
- }
function <funcName>(a, b){
if(a > b){
return 0;
}else{
return <funcName>(<next>(a), b) + <func>(a);
}
}
其中,funcName为函数名,next为取下一个a的值(根据“步长”走一步),func计算a。
我们很容易将这个代码翻译成javascript代码:
- function sum(term, a, next, b){
-
if(a > b){
-
return 0;
-
}else{
-
return sum(term, next(a), next, b) + term(a);
- }
- }
function sum(term, a, next, b){
if(a > b){
return 0;
}else{
return sum(term, next(a), next, b) + term(a);
}
}
这时候,如果我们传递给sum这个函数两个额外的参数term,和next,我们就可以得到想要的结果.
现在,我们的求和函数就可以写成这种形式:
- function intSum(a, b){
-
function inc(x){return x + 1;}
-
function identity(x){return x;}
-
-
return sum(identity, a, inc, b);
- }
function intSum(a, b){
function inc(x){return x + 1;}
function identity(x){return x;}
return sum(identity, a, inc, b);
}
同样,立方和的函数可以写成这样:
- function cubeSum(a, b){
-
function inc(x){return x + 1;}
-
function cube(x){return x * x * x;}
-
-
return sum(cube, a, inc, b);
- }
function cubeSum(a, b){
function inc(x){return x + 1;}
function cube(x){return x * x * x;}
return sum(cube, a, inc, b);
}
最后一个
- function piSum(a, b){
-
function piTerm(x){ return 1/((x+2)*x); }
-
function piNext(x){ return x+4; }
-
-
return sum(piTerm, a, piNext, b);
- }
function piSum(a, b){
function piTerm(x){ return 1/((x+2)*x); }
function piNext(x){ return x+4; }
return sum(piTerm, a, piNext, b);
}
可以看到,我们只需要传递给sum一个真正的计算算子和一个计算步长的算子就可以了。
现在给出一个简单的测试函数:
- function sumTester(){
-
print(intSum(1, 100));
-
print(cubeSum(1, 4));
-
print(piSum(1, 10000)*8);
- }
function sumTester(){
print(intSum(1, 100)); // should print 5050
print(cubeSum(1, 4)); // should print 100
print(piSum(1, 10000)*8); // should print PI
}
两者的运行结果都如下:
写道
js> sumTester()
5050
100
3.141392653591793
顺便说一下,这些例子均在rhino下测试,在浏览器中没有做过测试,如最后的测试函数中的print,估计浏览器中嵌入的js引擎不能正确解析。关于rhino,javaeye里有相关的文章,可以找找看。
使用高阶函数,我们可以在第一阶抽象的基础上再做一次抽象,生成一个函数的产生器,传递给一定的算子,即可完成各个相似的计算。
相关推荐
可维护JavaScript 开始吧 首先签出代码: git clone git@github.com:abruzzi/tw-testable-javascript.git 并初始化环境: cd tw-testable-javascript bower install 安装完成后,您应确保后端服务按预期工作: ...
Let's Go 服务器的基线这是一个简单的 RESTFul 服务器,用于 Let' Go 在线商店服务器,它... 安装 Ruby 后,您需要将此存储库克隆到本地环境: $ git clone git@github.com:abruzzi/letusgo-server.git$ cd letusgo-
Let's Go 服务器的基线这是一个简单的 RESTFul 服务器,用于 Let' Go 在线商店服务器,它... 安装 Ruby 后,您需要将此存储库克隆到本地环境: $ git clone git@github.com:abruzzi/letusgo-server.git$ cd letusgo-
Let's Go 服务器的基线这是一个简单的 RESTFul 服务器,用于 Let' Go 在线商店服务器,它... 安装 Ruby 后,您需要将此存储库克隆到本地环境: $ git clone git@github.com:abruzzi/letusgo-server.git$ cd letusgo-
### JavaScript函数式编程教程 #### 引言 随着软件开发技术的发展,JavaScript作为一种重要的前端开发语言,其在函数式编程领域的应用越来越广泛。本文通过一个简单的书签应用案例,介绍了如何利用JavaScript进行...
在 Jasmine 中开始使用 TDD 首先,您需要将此存储库克隆到本地计算机$ git clone git@github.com:abruzzi/tdd-boilerplate.git 然后转到为您创建的git文件夹: $ cd tdd-boilerplate 将您的specification添加到spec...
JavaScript支持高阶函数,即可以作为参数传递或返回其他函数的函数。这种编程风格在处理异步操作、事件处理和复杂业务逻辑时特别有用。 JavaScript内核系列的电子书是由abruzzi编写的,它覆盖了JavaScript的基础...
描述中提到的“博文链接:https://abruzzi.iteye.com/blog/306242”虽然没有具体信息,但通常博客会详细阐述实现过程,包括可能涉及的步骤、代码示例和遇到的问题。由于标签中有“源码”,我们可以期待这篇博客会...
$ git clone git@github.com:abruzzi/reveal-tw-boilerplate.git $ cd presentation $ open index.html 对于那些想要自定义主题本身的人 要使用它,只需克隆这个 repo 并: $ bundle install 安装所需的gem后,您...
JavaScript是一种基于原型的动态类型语言,虽然它不像Java或C++那样有内置的面向对象机制,但通过函数闭包的概念,开发者可以模拟出面向对象的编程模式。闭包是指一个函数能够访问并操作其外部作用域中的变量,即使...
"Michael", "Lincoln", "Sucre", "Abruzzi", "T-Bag", "C-Note", "Tweener", "Charles" }; ``` 这种方式不仅使代码更加紧凑,而且提高了代码的可读性和维护性。 ##### 3. 扩展方法 扩展方法提供了一种向现有类型...