真正理解/玩转柯里化函数和反柯里化

一. 在最初 去网上查了相关资料 很有意思一点就是 看到了这个函数在数学中的定义

函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名。
currying又称部分求值。一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值
从字面上理解currying并不太容易,下面通过编写一个计算每月开销的函数来解释函数柯里化currying

二. 在《JS设计模式》中是这样讲的

函数柯里化的思想是对函数的参数进行分割,有点像其他语言中的类的多态,就是根据传参不同,可以让一个函数存在多种状态,只不过柯里化处理的是函数,因此要实现函数的柯里化是要以函数为基础的,借助柯里化器伪造其他函数,让这些伪造的函数在执行时调用这个函数完成不同的功能。

// 函数柯里化
function curry(fn) {
	//缓存数组slice方法 Array.prototype.slice
	let Slice = [].slice;
	//从第二个参数开始截取参数
	let args = Slice.call(arguments, 1)
	//闭包返回新函数
	return function() {
		//类数组转数组
		let addArgs = Slice.call(arguments)//拼接参数
		allArgs = args.concat(addArgs);
		return fn.apply(null, allArgs)
	}
}

先测试下这个柯里化函数器。
创建一个累加器,然后再拓展。
累加器思想大概是: 当传递一个参数时相加一个固定数字, 当不传递时相加两个固定数字。

// 累加器
add = (num1, num2) => {
	return num1 + num2;
}
//加5加法
add5 = (num) => {
	return add(5, num)
}
//测试add
console.log(add(1, 2))     // 3
//测试add5
console.log(add5(6))     // 11
//柯里化函数创建加5累加器
let add5 = curry(add, 5)
console.log(add5(7))     // 12
// 7 + 8
let add7and8 = curry(add, 7, 8)
console.log(add7and8())     // 15

看, 通过柯里化器对add方法实现的多态拓展且不需要像以前那样明确声明函数了,因为函数的创建过程已经在柯里化器中实现了

三. 反柯里化

Array.prototype上的方法原本只能用来操作array对象。但用call和apply可以把任意对象当作this传入某个方法,这样一来,方法中用到this的地方就不再局限于原来规定的对象,而是加以泛化并得到更广的适用性。
有没有办法把泛化this的过程提取出来呢?反柯里化(uncurrying)就是用来解决这个问题的。反柯里化主要用于扩大适用范围,创建一个应用范围更广的函数。使本来只有特定对象才适用的方法,扩展到更多的对象。
uncurrying的话题来自JavaScript之父Brendan Eich在2011年发表的一篇文章。以下代码是 uncurrying 的实现方式之一:

Function.prototype.uncurrying = function () { 
  var _this = this;
  return function() {
    var obj = Array.prototype.shift.call( arguments );
    return _this.apply( obj, arguments );
  };
};

另一种实现方法如下

Function.prototype.currying = function() {
    var _this = this;
    return function() {
        return Function.prototype.call.apply(_this, arguments);
    }
}

最终是都把this.method转化成method(this,arg1,arg2…)以实现方法借用和this的泛化

下面是一个让普通对象具备push方法的例子

 var push = Array.prototype.push.uncurrying(),
    obj = {};
  push(obj, 'first', 'two');
  console.log(obj);
/*obj {
    0 : "first",
    1 : "two"
}*/

通过uncurrying的方式,Array.prototype.push.call变成了一个通用的push函数。这样一来,push函数的作用就跟Array.prototype.push一样了,同样不仅仅局限于只能操作array对象。而对于使用者而言,调用push函数的方式也显得更加简洁和意图明了

最后,再看一个例子

var toUpperCase = String.prototype.toUpperCase.uncurrying();
console.log(toUpperCase('avd')); // AVD
function AryUpper(ary) {
    return ary.map(toUpperCase);
}
console.log(AryUpper(['a', 'b', 'c'])); // ["A", "B", "C"]
发布了38 篇原创文章 · 获赞 1 · 访问量 559

猜你喜欢

转载自blog.csdn.net/weixin_43718291/article/details/103245882
今日推荐