什么是柯里化

1. 什么是柯里化

在Lambda演算中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个只有一个参数的函数,而这个函数返回一个带参数的函数,这样就实现了能写两个参数的函数了 ——这就是所谓的柯里化(Currying,以逻辑学家Hsakell Curry命名),也可以理解为一种在处理函数过程中的逻辑思维方式。
柯里化,用一句话解释就是,把一个多参数的函数转化为单参数函数的方法。

// 普通的add函数
function add(x, y) {
    return x + y
}
 
// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}

add(1, 2)           // 3
curryingAdd(1)(2)   // 3
// 分别就是 
var add2 = curryingAdd(1); 
add2(2)             //3

函数柯里化基本是在做这么一件事情:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。用公式表示就是我们要做的事情其实是

fn(a,b,c,d)=>fn(a)(b)(c)(d);

fn(a,b,c,d)=>fn(a,b)(c)(d);

fn(a,b,c,d)=>fn(a)(b,c,d)......

再或者这样:

fn(a,b,c,d)=>fn(a)(b)(c)(d)();

fn(a,b,c,d)=>fn(a);fn(b);fn(c);fn(d);fn()

但不是这样:

fn(a,b,c,d)=>fn(a);

fn(a,b,c,d)=>fn(a,b)

再看看改版后的

function add(x, y) {
    return x + y
}
 
const curry = (fn, ...arg) => {
    let all = arg;
    return (...rest) => {
        all.push(...rest);
        return fn.apply(null, all);
    }
}
let add2 = curry(add, 2)
add2(8);          //10
add2 = curry(add);
add2(2,8);        //10
curry(add, 2)(8)  //10

加入执行环境

const curry = (fn, constext, ...arg) => {
    let all = arg;
    return (...rest) => {
        all.push(...rest);
        return fn.apply(constext, all);
    }
}

window.a = 3
let cc = curry(add,window,1)
cc(2)   // 6
let obj = {a:100}
let cc = curry(add,obj,1)
cc(2)   // 103

2. 柯里化怎么用?

1. 参数复用

// 正常正则验证字符串 reg.test(txt)
 
// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}
 
check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true
 
// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}
 
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
 
hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

3. 利用reduce实现柯里化

// 实现一个add方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
 
function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);
 
    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };
 
    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

另一种实现

const curry = (fn, ...arg) => {
   let all = arg || [],
       length = fn.length;
   return (...rest) => {
       let _args = all.slice(0); //拷贝新的all,避免改动公有的all属性,导致多次调用_args.length出错
       _args.push(...rest);
       if (_args.length < length) {
           return curry.call(this, fn, ..._args);
       } else {
           return fn.apply(this, _args);
       }
   }
}
let add2 = curry(add, 2)
console.log(add2(8));  // 10
add2 = curry(add);
console.log(add2(2, 8)); // 10
console.log(add2(2)(8)); // 10
let test = curry(function(a, b, c) {
console.log(a + b + c);
})
test(1, 2, 3);  // 6
test(1, 2)(3); // 6
test(1)(2)(3); // 6
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值