# 掘金“最棒的”柯里化（curry)指南 | 函数式编程

### 柯里化的概念

1. 正常
// Object.prototype.toString.call(elements) === "[object Array]"
function getChildren(elements){
return elements.map(i => i.childNodes)
}

1. curry
// 先假设我们实现了柯里化函数 curry, 下面会讲curry的实现细节
// curry: (* → a) → (* → a)
//function curry(fn: (any)=> any):(any)=>any{}
//var map = curry(function(f, ary) {
//  return ary.map(f);
//});
let getChildren = map(i => i.childNodes)

### 柯里化实现

function a(b1, b2){}
console.log(a.length) // 2
// 柯里化后
let curryA = curry(a)
console.log(curryA.length) // 等下如果使用第一种实现，length = 0

#### 第一种实现

function curry(fn){
return function f(){
const args = [].slice.call(arguments)
if(args.length < fn.length){
return function(){
// 下面的arguments与上面的arguments不同
return f.apply(this, args.concat([].slice.call(arguments)))
}
}else{
return fn.apply(this, args)
}
}
}

#### 第二种实现

// n为还需接收的参数
var _arity = function (n, fn) {
/* eslint-disable no-unused-vars */
switch (n) {
case 0: return function() { return fn.apply(this, arguments); };
case 1: return function(a0) { return fn.apply(this, arguments); };
case 2: return function(a0, a1) { return fn.apply(this, arguments); };
case 3: return function(a0, a1, a2) { return fn.apply(this, arguments); };
case 4: return function(a0, a1, a2, a3) { return fn.apply(this, arguments); };
case 5: return function(a0, a1, a2, a3, a4) { return fn.apply(this, arguments); };
case 6: return function(a0, a1, a2, a3, a4, a5) { return fn.apply(this, arguments); };
case 7: return function(a0, a1, a2, a3, a4, a5, a6) { return fn.apply(this, arguments); };
case 8: return function(a0, a1, a2, a3, a4, a5, a6, a7) { return fn.apply(this, arguments); };
case 9: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8) { return fn.apply(this, arguments); };
case 10: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { return fn.apply(this, arguments); };
default: throw new Error('First argument to _arity must be a non-negative integer no greater than ten');
}
}

// curry使用_curry1柯里化，接收参数fn, 默认调用curryN,
// curry = (fn) => curryN(fn.length, fn)
var curry = _curry1(function(fn) {
return curryN(fn.length, fn);
});
// 如果是一个参数，使用_curry1, ramda里面的内部函数，接收的参数大多为1-3个
var _curry1 = function (fn) {
return function f1(a) {
if (arguments.length === 0) {
return f1;
} else {
return fn.apply(this, arguments);
}
};
}
// 如果参数是2个
_curry2 = function(fn) {
return function f2(a, b){
switch(arguments.length){
// 返回本身
case 0:
return f2;
// 返回_curry1的结果,
case 1:
return _curry1(function(_b) { return fn(a, _b); })
// 直接调用
case 2:
return fn(a, b)
}
}
}
// curryN本身也是一个柯里化的函数
var curryN = _curry2(function(n, fn){
if (length === 1) {
return _curry1(fn);
}
// 使用_artity包裹函数_curryN, _curryN包括3个参数，具体看下面的实现
return _arity(length, _curryN(length, [], fn));
})
// 内部_curryN的实现， 在上面第一种方式，的基础上添加包裹函数
var _curryN = function (length, recived, fn) {
return function() {
// 获取函数调用的参数
var args = [].slice.call(arguments);
// 已传的参数
var combined = recived.concat(args);

if(combined.length < length ) {
return _arity(length - combined.length, _curryN(length, combined, fn));

} else {
return fn.apply(this, combined);
}
}
}

### 参考文档

1. 基础知识，我参考了这里 js函数式编程指南
2. ramda的源码解析，我参考了这篇掘金文章 Ramda.js中的柯里化实现
3. ramda的源码链接 curry.js