JavaScript函數柯里化


函數式

JavaScript是以函數為一等公民的函數式語言。函數在JavaScript中也是一個對象(繼承制Function),函數也可以作為參數傳遞成函數變量。最近幾年函數式也因為其無副作用的特性、透明性、惰性計算等在高並發,大數據領域火起來了。

JavaScript中也有如Underscore、lodash之類的函數式庫,如lodash的使用方式:

var names = _.chain(users)
  .map(function(user){
    return user.user;
  })
  .join(" , ")
  .value();
console.log(names);

關於lodash更多內容請參考JavaScript工具庫之Lodash.

柯里化

今天文章將以高階函數中的柯里化方式來,看看JavaScript的函數式能力。

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受余下的參數且返回結果的新函數的技術。這個技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,盡管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。

在理論計算機科學中,柯里化提供了在簡單的理論模型中比如只接受一個單一參數的lambda 演算中研究帶有多個參數的函數的方式。

JavaScript的柯里化實現

下邊的例子,我們將把柯里化方式泛化為接受任意個參數,直到聲明的方法參數個數飽和才執行,所以根據參數個數可以有多種柯里化函數產生。

代碼如下:

(function(global) {
    var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m,
        FN_ARG_SPLIT = /,/,
        FN_ARG = /^\s*(_?)(\S+?)\1\s*$/,
        STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    var getArgLength = function(func) {
        var fnText = func.toString().replace(STRIP_COMMENTS, '');
        var argDecl = fnText.match(FN_ARGS);
        var params = [];
        argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) {
            arg.replace(FN_ARG, function(all, underscore, name) {
                params.push(name);
            });
        });
        return params.length;
    };
    var curryFunc = function(func, len) {
        len = len || getArgLength(func);
        var args = [];
        if (len === 0) {
            return func.apply(null);
        }
        return function() {
            [].push.apply(args, [].slice.apply(arguments));
            if (args.length >= len) {
                return func.apply(null, args);
            }
            return arguments.callee;
        };
    };
    global.curryFunc = curryFunc;
})(this);
function add(x, y, z) {
    return x + y + z;
}
console.log("result 1:", curryFunc(add)(1, 2)(3));
console.log("result 2:", curryFunc(add)(1)(2, 3));
console.log("result 3:", curryFunc(add)(1)(3)(2));
function add(x, y, z) {
    return x * y * z;
}
console.log("result 1:", curryFunc(add)(2, 4)(6));
console.log("result 2:", curryFunc(add)(2)(4, 6));
console.log("result 3:", curryFunc(add)(2)(6)(4));
function sayHello() {
    return "hello";
}
console.log(curryFunc(sayHello));

首先上面會利用正則來獲取傳入函數的參數個數。再返回一個函數的代理,每次的調用都會將傳入參數緩存在args臨時變量中,直到參數個數飽和才會立即執行。代碼比較冗長,慢慢品味,當然有不足之處,也希望大家指出來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM