最近幾天在看高程,對於其中的概念有些疑惑,所以找了相關的教程,但願本身可以瞭解它,而且有可能運用它。本文主要參考的是冴羽的博客,結合自身能理解的方式來熟悉這兩個概念。javascript
什麼是柯里化呢?來自於冴羽的博客-JavaScript專題之函數柯里化html
在數學和計算機科學中,柯里化是一種將使用多個參數的一個函數轉換成一系列使用一個參數的函數的技術。java
本例子來自於Fun Fun Function-YouTubegit
let dragon = (name, size, element) => {
return `${name} is a ${size} dragon that breathes ${element}!`;
};
console.log(dragon("Karo", "large", "ice"));
// Karo is a large dragon that breathes ice!
複製代碼
let dragon = name => size => element =>
`${name} is a ${size} dragon that breathes ${element}!`;
console.log(dragon('Karo')('large')('ice'));
// Karo is a large dragon that breathes ice!
複製代碼
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
let dragon = (name, size, element) => {
return `${name} is a ${size} dragon that breathes ${element}!`;
};
dragon = curry(dragon);
let fluffykinsDragon = dragon('fluffykins'); // curyy函數只能保存一個參數就返回了
// fluffykins is a undefined dragon that breathes undefined!
// 若是執行下面語句就會報錯,
// let tinyDragon = fluffykinsDragon("tiny"); 報錯
複製代碼
藉助上面的例子,咱們開始從初版寫起github
// 初版
function curry(fn) {
var args = [];
return function() {
args = args.concat([].slice.call(arguments));
return function() {
args = args.concat([].slice.call(arguments));
return function() {
args = args.concat([].slice.call(arguments));
return fn.apply(null, args);
}
}
};
}
複製代碼
測試一下app
// 測試
let dragon = (name, size, element) => {
return `${name} is a ${size} dragon that breathes ${element}!`;
};
dragon = curry(dragon);
let fluffykinsDragon = dragon("fluffykins");
let tinyDragon = fluffykinsDragon("tiny");
console.log(tinyDragon("ice"));
// fluffykins is a tiny dragon that breathes ice!
複製代碼
若是參數過多,那麼就會無限嵌套,所以第二版用遞歸優化一下函數
// 第二版
function curry(fn, args, length) {
length = length || fn.length;
args = args || [];
return function() {
args = args.concat([].slice.call(arguments));
if (arguments.length < length) {
return curry(fn, args, length - arguments.length);
}
return fn.apply(this, args);
}
}
複製代碼
咱們發現curry(fn, args, length - 1)
有三個參數,咱們利用高程中的例子當成中間函數能夠再優化一下,因而有了第三版測試
// 第三版
function sub_curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
function curry(fn, length) {
length = length || fn.length;
return function() {
if (arguments.length < length) {
var args = [fn].concat([].slice.call(arguments));
return curry(sub_curry.apply(this, args), length - arguments.length);
}
return fn.apply(this, arguments);
};
}
複製代碼
function sub_curry(fn, ...args) {
return (...args1) => fn(...args, ...args1);
}
function curry(fn, length) {
length = length || fn.length;
return (...args) => {
if (args.length < length) {
return curry(sub_curry(fn, ...args), length - args.length);
}
return fn(...args);
};
}
複製代碼
以前我把args
參數進行柯里化了,如今除去length
參數的另外一種寫法優化
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function() {
var _args = args.concat([].slice.call(arguments));
if (_args.length < length) {
return curry.call(this, fn, _args);
}
return fn.apply(this, _args);
};
}
複製代碼
function curry(fn) {
return judge = (...args) => {
return args.length === fn.length ? fn(...args) : (...arg) => judge(...args, ...arg);
};
}
複製代碼
參數含有佔位符ui
// 第四版
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function() {
var newArgs = [].slice.call(arguments);
for (var i = 0, len = args.length; i < len; i++) {
if (args[i] === _) {
args.splice(i, 1, newArgs.shift());
}
if (newArgs.length === 0) break;
}
var _args = args.concat(newArgs);
var _filterArr = _args.filter(ele => ele !== _);
if (_filterArr.length < length) {
return curry.call(this, fn, _args);
}
return fn.apply(this, _args);
};
}
複製代碼
測試一下
var fn = curry(function(a, b, c, d, e) {
console.log([a, b, c, d, e]);
});
var _ = {};
// 輸出的結果都是[1, 2, 3, 4, 5]
fn(1, 2, 3, 4, 5);
fn(_, 2, 3, 4, 5)(1);
fn(1, _, 3, 4, 5)(2);
fn(1, _, 3)(_, 4)(2)(5);
fn(1, _, _, 4)(_, 3)(2)(5);
fn(_, 2)(_, _, 4)(1)(3)(5);
複製代碼
什麼是偏函數呢?仍是來自於冴羽的博客
在計算機科學中,局部應用是指固定一個函數的一些參數,而後產生另外一個更小元的函數。
柯里化是將一個多參數函數轉換成多個單參數函數,也就是將一個 n 元函數轉換成 n 個一元函數。
局部應用則是固定一個函數的一個或者多個參數,也就是將一個 n 元函數轉換成一個 n - x 元函數。
// 也就是高程中的例子
function partial(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
複製代碼
var _ = {};
function partial(fn) {
var args = [].slice.call(arguments, 1);
return function() {
var len = args.length;
var _args = [].slice.call(arguments);
for(var i = 0; i < len; i++) {
args[i] = args[i] === _ ? _args.shift() : args[i];
if (_args.length === 0) break;
}
args.concat(_args);
return fn.apply(this, args);
}
}
複製代碼
測試一下
var subtract = function(a, b, c) {
return b - a + c;
};
var subFrom20 = partial(subtract, 5, _, _);
console.log(subFrom20(15, 5, 5)); // 15
複製代碼
經過教程和本身的理解初步瞭解了柯里化和偏函數,至於具體的使用場景,多是用到了才知道吧。