js 函数柯里化

  1. js 函数柯里化
  2. 什么是函数柯里化
  3. 简单实现

js 函数柯里化

什么是函数柯里化

维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

通过一个简单的例子解释下,比如有一个函数:

function addFn (x, y, z) {
  return x + y + z
}

正常函数的调用是:

addFn(1, 2, 3)

而柯里化后的函数调用应该像这样:

addFn(1)(2)(3)

// 或者
addFn(1, 2)(3)

// 又或者
addFn(1)(2, 3)

这样怎么实现呢,通过简单的2个参数的加法函数演示一下,当然这是最简单的,调用的方式也只能传单个参数;

function curryAdd (x) {
  return function (y) {
    return x + y
  }
}

curryAdd(1)(2)    // 3    

那么我们怎么怎么把一个普通的函数转换成形如上述curryAdd函数的样子呢,这就需要我们进行函数的柯里化,将一个多参数的函数转变称为能够像链式调用那样的函数;

抽象上面的curryAdd高阶函数,先假设只调用两层(fn(1)(2)),也可以理解为被柯里化的函数只有两个参数,我们最多只能返回两层,args1currying函数本身的参数,args2是第一层的参数1args3是第二层的参数2

function addFn (x, y) {
  return x + y
}

function currying (fn, ...args1) {
  console.log(args1, 'args1')   // 由于没有传参这里是[]
  return function (...args2) {
    console.log(args2, 'args2') // 这里是[1]
    return function(...args3) {
      console.log(args3, 'args3') // 这里是 [2]

      // 然后把接收到的所有参数,交由fn函数处理,然后返回
      return  fn(...args1, ...args2, ...args3)
    }
  }
}

let fn = curry(addFn)
console.log(fn(1)(2))  // 3

简单实现

问题来了,我们不可能有多少个参数就写多少层吧,我们需要定义一个柯里化函数,来进行函数的柯里化,具备通用性,无论多少参数都能进行转化;

我们可以利用函数上的特殊属性length来进行处理,这个是函数参数的个数,比如我们定义一个函数foo(a, b, c)foo.length 就是函数入参的个数3

由于我们不知道具体有多少个参数,因此需要递归来进行处理,如果传入的参数没有超过函数入参的个数,就再次调用自己,进行下一轮的柯里化,直到传入的参数等于函数入参的个数

代码如下:

function simpleCurrying(fn, ...args) {
  // 如果传入的参数已经大于函数可以处理的长度,则直接抛出错误
  if (args.length > fn.length) {
    throw new Error('参数超出可处理范围')
  }

  // 一旦传入的参数等于了函数可以处理的长度,则调用fn进行处理
  if (args.length === fn.length) {
    return fn(...args)
  }

  // 否则继续调用自己,注意需要在外层接收下一次调用的入参,并合并到上一次的参数中,否则参数会丢失
  return function(...args2) {
    return simpleCurrying(fn, ...args, ...args2)
  }
}

让我们来测试一下:

function add (a, b, c, d) {
  return a + b + c + d
}

let sc = simpleCurry(add);
sc(1, 2, 3, 4)  // 10
sc(1, 2)(3)(4)  // 10
sc(1, 2, 3)(4)  // 10
sc(1, 2)(3, 4)  // 10
sc(1, 2, 3, 4, 5) // ERROR 参数超出可处理范围

当然我们的例子还有许多需要优化的地方,而lodash.curry也提供了柯里化函数的方法,可以直接使用;


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com