js - 理解函数柯里化

Posted 耳东蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js - 理解函数柯里化相关的知识,希望对你有一定的参考价值。

怎么理解柯里化

柯里化的意思就是将一个多元函数,转换成一个依次调用的单元函数。

curry 的这种用途可以理解为:参数复用。本质上是降低通用性,提高适用性。

f(a,b,c)f(a)(b)(c)

var add = function(x) 
  return function(y) 
    return x + y;
  ; 
;
const increment = add(1);

increment(10); // 11
var  curry  = require('lodash')

var abc = function(a, b, c) 
  return [a, b, c];
;
 
var curried = curry(abc);
 
console.log(curried(1)(2)(3));
// => [1, 2, 3]
 
var person = [name: 'kevin', age: 12 , name: 'daisy', age: 13]

var prop = curry(function (key, obj) 
  return obj[key]
);

// 普通方式
var name = person.map(function (item) 
    return item.name;
)

// 柯里化方式
var name = person.map(prop('name'))
console.log(name) // [ 'kevin', 'daisy' ]

var age = person.map(prop('age'))
console.log(age) // [ 12, 13 ]




上面代码,我们将返回对象属性的部分通过函数编程实现。

var prop = curry(function (key, obj) 
  return obj[key]
);

curry函数将传入的function内的两个参数。变成两个使用一个参数的函数执行。

function(key, obj) 

// 根据柯里化转换转换成两个函数,并且第一个参数调用之后返回的还是一个函数,参数为原函数的第二个参数。

props(key) => function(obj)

精髓理解:用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数

手写柯里化

初始版本


var abc = function(a, b, c) 
  return [a, b, c];
;

function customerCurry(fn) 
  const fnArgsCount = fn.length;  	// fn函数的形参个数
  let curryArgs = [];			// 柯里化函数已经传入的实惨个数
  
  function wrapperFunction () 
    curryArgs = curryArgs.concat([...arguments])
      
    if (curryArgs.length < fnArgsCount) 
      return wrapperFunction;
     
      
     return fn.apply(this, curryArgs)
   
  
   return wrapperFunction;


var incre = customerCurry(abc)
incre(1)(2)(3)  // 正常返回 [1, 2, 3]

incre() 				// 返回 [1, 2, 3]


上面的代码可以正常返回,但是还是存在问题。


只能执行一次,incre不能多次执行。这里因为上面书写的方式将参数统一记录下来了。customerCurry参数是最外层必包在维护。所以在incre(1)(2)(3) 执行完之后。内部参数个数已经达到三个


第二版本


var abc = function(a, b, c) 
  return [a, b, c];
;

function customerCurry(fn) 
  	const fnArgsCount = fn.length;  	// fn函数的形参个数
  	let curryArgs = [];								// 柯里化函数已经传入的实惨个数
  
		if (fnArgsCount <= 1) 
    	throw new Error('函数至少传入两个参数');
    
  
  	function wrapperFunction () 
    	curryArgs = [...arguments];
      
      if (curryArgs.length < fnArgsCount) 
      	return eval(`wrapperFunction.bind(this, $curryArgs.reduce(function(x, y) return x + ',' + JSON.stringify(y);) )`);
      
      
      return fn.apply(this, curryArgs)
    
  
  	return wrapperFunction;


var incre = customerCurry(abc)
incre(1)(2)(3)  // 正常返回 [1, 2, 3]


当然这种还是有缺陷,因为对于参数的传递,这里处理不是很好,使用的是json的方法。


第三版本


var abc = function(a, b, c) 
  return [a, b, c];
;

function customerCurry(fn) 
  	const fnArgsCount = fn.length;  	// fn函数的形参个数
  	const curryWrapperArgs = [...arguments].slice(1);
  
  	function wrapperFunction () 
    	let curryArgs = [fn, ...curryWrapperArgs, ...arguments];
      
      if (curryArgs.length < fnArgsCount + 1) 
      	return customerCurry.apply(this, curryArgs);
      
      
      return fn.apply(this, curryArgs.slice(1))
    
  
  	return wrapperFunction;


var incre = customerCurry(abc)
incre(1)(2)(3)  // 正常返回 [1, 2, 3]



第四版本

这样写起来没什么问题,但是既然学到了柯里化,函数式编程的思想去书写一下。

例子来源:https://github.com/mqyqingfeng/Blog/issues/42

// 第二版
function sub_curry(fn) 
    var args = [].slice.call(arguments, 1);
    return function() 
        return fn.apply(this, args.concat([].slice.call(arguments)));
    ;


function curry(fn, length) 

    length = length || fn.length;

    var slice = Array.prototype.slice;

    return function() 
        if (arguments.length < length) 
            var combined = [fn].concat(slice.call(arguments));
            return curry(sub_curry.apply(this, combined), length - arguments.length);
         else 
            return fn.apply(this, arguments);
        
    ;


执行顺序:


换一个方向:



现在我们用函数式编程的思想来理解一下。


第五版本:高颜值写法

var curry = fn =>
    judge = (...args) =>
        args.length === fn.length
            ? fn(...args)
            : (arg) => judge(...args, arg)

参照

  • https://github.com/mqyqingfeng/Blog/issues/42
  • https://juejin.cn/post/6844903936378273799#heading-14

以上是关于js - 理解函数柯里化的主要内容,如果未能解决你的问题,请参考以下文章

[转]js函数式变成之函数柯里化

js中的柯里化

Scala函数柯里化(Currying)

Scala函数柯里化(Currying)

Scala函数柯里化(Currying)

Scala函数柯里化(Currying)