Javascript中currying的實現

longmanma發表於2021-09-09

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         args.push(arguments[i]);
    }
    return function() {
        for (var i = 0; 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         args.push(arguments[i]);
    }
    return function() {
        for (var i = 0; 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 );

圖片描述

 

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2730/viewspace-2800202/,如需轉載,請註明出處,否則將追究法律責任。

相關文章