Currying好像是函數式語言都有的一個特性,比如Perl,Python,Javascript。
那麼到底什麼是Currying,我是在學習Closure時無意中接觸到這個定義的,覺得很是有趣。
先看看 Wiki 中的定義:
Currying is the technique of transforming a function that takes multiple arguments
in such a way that it can be called as a chain of functions each with a single argument.
大概的意思就是說,將擁有多個參數的函數Currying化為擁有單一參數的函數形式。
下面舉一個簡單的例子說明Javascript中的Currying實現,一個簡單的求和函數:
function add(x, y) {
return x + y;
}
console.log('add(2, 3) == ' + add(2, 3));
對其進行Currying,及調用方法:
function curry_add(x) {
return function(y) {
return x + y;
}
}
console.log('curry_add(2)(3) == ' + curry_add(2)(3));
注意,curry_add(2) 返回的是函數。
我們還可以定義一個通用的 curry 函數:
function curry(fn) {
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
return function() {
for (var i
= 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
return fn.apply(window, args);
}
}這個函數至少接收一個參數(需要curry的函數),對於前面的 add 函數,我們可以這樣來調用:console.log('curry(add)(2, 3) == ' + curry(add)(2, 3));
console.log('curry(add, 2)(3) == ' + curry(add, 2)(3));
console.log('curry(add, 2, 3)() == ' + curry(add, 2, 3)());
因為 curry(add, 2) 或 curry(add) 返回的還是函數,所以我們還可以對其進行Currying,如下代碼:
console.log('curry(curry(add), 2)(3) == ' + curry(curry(add), 2)(3));
console.log('curry(curry(add, 2), 3)() ==' + curry(curry(add, 2), 3)());
運行時:
代碼下載
[update_2009-2-17]
按照 @winter-cn 的提示,我來到另外一篇討論Currying的 文章 ,發現那裡的做法是寫一個可以 Chain 的Currying,
另外我還發現了一個我以前不知道的特性,add.length 返回的是函數形式參數的個數,比如這個例子中的 add.length == 2
這就好辦了,我們可以根據傳遞進來的參數的多少來判斷是否返回執行結果或者是返回函數。
我大概的想法是要用到遞迴,來看看我的實現:
function curry2(fn) {
var args = [];
for
(var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
return function() {
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
if (args.length >= fn.length) {
return fn.apply(window, args);
}
else {
return curry2.apply(window, [fn].concat(args));
}
}
}
console.log('curry2(add)(2, 3) == ' + curry2(add)(2, 3));
console.log('curry2(add)(2)(3) == ' + curry2(add)(2)(3));
當然這樣的Currying技術,只能用在有明確形式參數的函數中,如果在add函數中使用arguments來捕獲參數,則這種Currying是行不通的。
上面的add可能不是很明顯,來看看擁有 4 個參數的add2函數,以及使用Currying技術:
function add2(x, y, z, k) {
return x + y + z + k;
}
console.log('curry2(add2)(1, 2, 3, 4) == ' + curry2(add2)(1, 2, 3, 4));
console.log('curry2(add2)(1, 2, 3)(4) == ' + curry2(add2)(1, 2, 3)(4));
console.log('curry2(add2)(1, 2)(3, 4) == ' + curry2(add2)(1, 2)(3, 4));
console.log('curry2(add2)(1)(2, 3, 4) == ' + curry2(add2)(1)(2, 3, 4));
console.log('curry2(add2)(1)(2)(3, 4) == ' + curry2(add2)(1)(2)(3, 4));
console.log('curry2(add2)(1)(2)(3)(4) == ' + curry2(add2)(1)(2)(3)(4));
更新的代碼下載
附:John Resig在Pro Javascript一書中關於Currying的實現代碼:
// A function that generators a new function for adding numbers
function addGenerator( num ) {
// Return a simple function for adding two numbers
// with the first number borrowed from the generator
return function( toAdd ) {
return num + toAdd
};
}
// addFive now contains a function that takes one argument,
// adds five to it, and returns the resulting number
var addFive = addGenerator( 5 );
// We can see here that the result of the addFive function is 9,
// when passed an argument of 4
alert( addFive( 4 ) == 9 );