当前位置:编程学习 > JAVA >>

为你的 Javascript 加点咖喱

当然,这篇文章不是一篇食谱,以在下的厨艺,目前只能停留在吃咖喱的阶段。咖喱的英文写作 Curry ,这个词还有另一层含义,为一种基于 Haskell 的实验式的函数编程语言,混合了 函数 与 逻辑编程 ,也加入了 约束编程 的特性。取名于数学家 Haskell Curry 。不得不说这位数学家对计算机的影响还真是很大。
 
However, 这里要谈的不是这位伟大的数学家,也不是 Haskell 语言,下面我先描述一个场景。
 
Javascript 作为一种函数式的编程语言,函数的使用称之为应用 ( applied ),有一定 JS 编程经验的开发者很快能想到内置的 Function.prototype.apply() 方法,在应用函数的时候,我们可以这样写:
 
 
 
1 var sayHello = function(param) {
2     return 'Hello, ' + param;        
3 }
5 sayHello.apply(null, ['Tom']);      // 'Hello, Tom'
 
 
sayHello() 内部的 this 指向全局对象。或者也可以让 JS 为我们完成数学运算:
 
1 function add(x, y) {
2     return x + y;
3 }
5 add.apply(null, [1, 2]);    // 3
这里应用函数的时候,我们一次性将所有的参数都传了进去,然而在实际应用中,我们可能会遇到不需要一次传入所有参数的情况,若 add() 函数的写法不变的话,只传入一个参数的时候就会产生错误,这不是我们希望看到的。
 
我们希望看到什么呢,在这里我们先 YY 一下:
 
 
1 var add = function(x, y) {
2     return x + y;
3 }
5 // 部分应用
6 var partialAdd = add.partialApply(null, [1]);
7 partialAdd.apply(null, [2]);    // 3
 
部分应用的步骤为我们提供了一个可供调用的新函数,随后我们可以使用其他参数来调用这个新函数。看起来很美吗?可惜的是,Javascript 中并没有 partialApply() 这样的方法和函数。不过前端开发者都是有进取精神的小强,再加上 JS 是一门灵活性很强的语言,我们完全可以构造出这样的部分应用函数,来满足我们的需求。
 
扯了这些,和之前提到的咖喱有关系吗?有的,这种是函数理解并处理部分应用场景的过程我们称之为 Curry 过程(也叫做函数的柯里化)。(别喷我,写一篇能让人有兴趣看下去的文章好难,不写点吸引人眼球的东西没人看啊)
 
回到之前所说,对于一个简短的加法运算,比如 1 + 2,部分应用的实现思路可以写成 add(1)(2),这就要求我们在调用 add(1) 后不仅不会产生错误,更要返回一个能够继续传入第二个参数的函数,说到这里答案已经呼之欲出了:
 
 
 
 
 1 function add(x, y) {
 2     // 部分
 3     if (typeof y === 'undefined') {
 4         return function(y) {
 5             return x + y;
 6         }
 7     }
 8 
 9     // 完全
10     return x + y;
11 }
 
 
 
其实这是一种常见的函数变换技巧,称为函数的不完全调用 (partial application)。这种函数变换的特点是每次调用都返回一个参数,直到得到最终运行结果为止。不过我们能用一种更为通用的方法来处理相同的任务吗,答案自然是肯定的:
 
 
function curry(fn) {
    var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
    return function() {
        var new_args = slice.call(arguments),
            args = stored_args.concat(new_args);
        return fn.apply(null, args);
    }
}
 
这样,在返回的函数中便保存了已经传入的参数,保存在变量 a 中,在返回的函数开头,剥离了第一个参数,因为这个参数是即将被 curry 化的函数,同时也保存了指向 slice() 方法的私有引用。当我们访问返回的函数时,新函数将原有的部分应用参数合并到新参数,再将合并后的参数应用到原始的函数中。
 
这样,使任意函数 curry 化的通用方法就有了,可将之前定义的 add() 函数用来测试。
 
 
// 普通函数
function add(x, y) {
    return x + y;
}
        
// 将一个函数 curry 化以获得一个新的函数
var newadd = curry(add, 1);
newadd(2);
        
// 另一种方法
curry(add, 3)(4);
        
// 连续 curry 化
var newadd = curry(add, 1);
var anothernewadd = curry(newadd, 2);
 
实际上,这些只是抛砖引玉,curry 化可改进的地方还有很多,比如当对参数的类型和顺序有要求时如何根据实际情况编写适合的 curry 化函数等。后续我会对内容作更多的补充,也欢迎各位畅所欲言,多提意见。
补充:web前端 , JavaScript ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,