柯里(Curry)
函數應用(Function Application)
// define a function
var sayHi = function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
};
// invoke a function
sayHi(); // "Hello"
sayHi('world'); // "Hello, world!"
// apply a function
sayHi.apply(null, ["hello"]); // "Hello, hello!"
正如你看到的,無論調用(invoking)一個函數或者應用(applying)一個函數,結果都是一樣的。var alien = {
sayHi: function (who) {
return "Hello" + (who ? ", " + who : "") + "!";
}
};
alien.sayHi('world'); // "Hello, world!"
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
在這段代碼中,在 sayHi()中this指向alien。在前一個例子中this指向的是全局對象(global object)。正如這兩個例子展示的,原來我們認爲的調用一個函數不僅僅是語法糖,等同於函數應用。
// the second is more efficient, saves an array
sayHi.apply(alien, ["humans"]); // "Hello, humans!"
sayHi.call(alien, "humans"); // "Hello, humans!"
部分函數應用(Partial Application)
現在我們知道調用一個函數實際上就是將一組參數應用於一個函數。// for illustration purposes
// not valid JavaScript
// we have this function
function add(x, y) {
return x + y;
}
// and we know the arguments
add(5, 4);
// step 1 -- substitute one argument
function add(5, y) {
return 5 + y;
}
// step 2 -- substitute the other argument
function add(5, 4) {
return 5 + 4;
}
在這個代碼片段中,第一步和第二步在JavaScript中是不合法的,但這是教你如何動手解決這個問題。var add = function (x, y) {
return x + y;
};
// full application
add.apply(null, [5, 4]); // 9
// partial application
var newadd = add.partialApply(null, [5]);
// applying an argument to the new function
newadd.apply(null, [4]); // 9
正如你看到的,部分函數應用給我們另外一個函數,可以在後面用其餘參數調用的函數。柯里化(currying)
// a curried add()
// accepts partial list of arguments
function add(x, y) {
var oldx = x, oldy = y;
if (typeof oldy === "undefined") { // partial
return function (newy) {
return oldx + newy;
};
}
// full application
return x + y;
}
// test
typeof add(5); // "function"
add(3)(4); // 7
// create and store a new function
var add2000 = add(2000);
add2000(10); // 2010
在這段代碼中,在第一次調用add()時候,會創建一個包裹作爲返回值的內部函數的閉包。這個閉包會儲存原始的 x 和 y 到私有的變量 oldx 和 oldy。// a curried add
// accepts partial list of arguments
function add(x, y) {
if (typeof y === "undefined") { // partial
return function (y) {
return x + y;
};
}
// full application
return x + y;
}
在這個例子中,函數add()它自身維護部分函數應用。但是我們能實現更加的流行的泛型實現嗎?function schonfinkelize(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);
};
}
schonfinkelize()可能比應該的情況複雜了點,因爲arguments在JavaScript中不是一個真正的數組。// a normal function
function add(x, y) {
return x + y;
}
// curry a function to get a new function
var newadd = schonfinkelize(add, 5);
newadd(4); // 9
// another option -- call the new function directly
schonfinkelize(add, 6)(7); // 13
轉換函數schonfinkelize()並不僅限於一個單獨的參數和一級的柯里化。有更多的用法:// a normal function
function add(a, b, c, d, e) {
return a + b + c + d + e;
}
// works with any number of arguments
schonfinkelize(add, 1, 2, 3)(5, 5); // 16
// two-step currying
var addOne = schonfinkelize(add, 1);
addOne(10, 10, 10, 10); // 41
var addSix = schonfinkelize(addOne, 2, 3);
addSix(5, 5); // 16