函数式编程之柯里化

Posted 红领巾

tags:

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

curry柯里化

首先我们先来看一个问题,如果实现一个add函数,可实现下面的功能

add(1,2,3) // 6
add(1)(2)(3) // 6
add(1,2)(3) // 6
add(1,2,3,4)(5)(6,7) // 28

当然了,所需要用到的知识点便是柯里化。

首先看下柯里化定义:
用于缓存函数参数的一种方式;给函数分步传递参数,每次传递部分参数,并返回一个更具体的函数接收剩下的参数,这中间可嵌套多层这样的接收部分参数的函数,直至返回最后结果。

初看上面这行字,可能不太好理解,我大概总结了下,如何去实现或者说去理解柯里化,大致是下面两点。

1、如何进行参数的缓存
2、在何时执行原函数
当收集到所需要的参数时,便去执行目标函数,得到结果,否则继续收集参数。

这里就不展开讨论闭包、call、apply、bind这些概念了。

举个例子

function add(a,b,c) {
  console.info(a, b, c, \'==此函数需要传递三个参数执行==\')
  return a + b + c
}

不难看出上面的函数执行需要三个参数,因此我们在调用test时,需要传递a,b,c否则函数执行异常;这时候回到上面的两点,如何进行参数的缓存以及何时执行原函数去得到目标结果。

接下来看看如何去实现

固定参数个数的柯里化

/**
 * 传入固定参数长度的柯里化
 * @params fn 原函数
 * @params  args 初始化参数
 */
function curry(fn, ...args) {
  const len = fn.length
  return function() {
    // 参数收集
    const allArgs = [...args, ...arguments]
    // 达到目标参数个数后,执行原函数
    if (allArgs.length >= len) {
      return fn.apply(null, allArgs)
    }
    return curry.call(null, fn, ...allArgs)
  }
}

function add(a,b,c) {
  return [...arguments].reduce((pre, cur) => pre += cur, 0)
}

let test = curry(add)

console.info(test(1,2,3)) // 6
console.info(test(1,2)(3)) // 6
console.info(test(1)(2)(3)) // 6

test = null // 防止内存泄漏

参数不固定

1、当传入空的参数时,执行原函数
/**
 * 传入参数不固定,参数为空时执行函数的柯里化
 * @params fn 原函数
 * @params  args 初始化参数
 */
function _curry(fn, ...args) {
  let allArgs = [...args]
  return function next(..._args) {
    allArgs = [...allArgs, ..._args]
    if (_args.length === 0) {
      return fn.apply(null, allArgs)
    }
    return next
  }
}

function _add() {
  return [...arguments].reduce((pre, cur) => pre += cur, 0)
}

const _test = _curry(_add)

_test(1,2)
_test(3)
console.info(_test()) // 6

_test(4)
console.info(_test()) // 10

_test = null // 防止内存泄漏
2、直接返回

这里需要用到一个小知识点,在解析函数的原始值时,会用到函数的隐式转换toString,因此我们的执行原函数节点在于toString

/**
 * 传入参数不固定柯里化
 * @params fn 原函数
 * @params  args 初始化参数
 */
function _curry(fn, ...args) {
  let allArgs = [...args]
  function next(..._args) {
    allArgs = [...allArgs, ..._args]
    return  _curry.call(null, fn, ...allArgs)
  }
  next.toString = function() {
    return fn.apply(null, allArgs)
  }
  return next
}

function _add() {
  return [...arguments].reduce((pre, cur) => pre += cur, 0)
}

const _test = _curry(_add)

_test(1, 2) // 2

_test(4)(5)(6) // 15
console.info(_test()) // 10

_test = null // 防止内存泄漏

上述便是柯里化用于延迟执行的几种用法

参数复用

能够理解上述用法后,柯里化用于参数复用就轻而易举了,如下例

/**
 * Object.prototype.toString.call(obj) === \'[object ***]\'
 */
function typeOf(value) {
  return function(obj) {
    const toString = Object.prototype.toString
    const map = {
      \'[object Boolean]\': \'boolean\',
      \'[object String]\': \'string\',
      \'[object Number]\': \'number\',
      \'[object Function]\': \'function\',
      \'[object Array]\': \'array\',
      \'[object Date]\': \'date\',
      \'[object RegExp]\': \'regExp\',
      \'[object Undefined]\': \'undefined\',
      \'[object Null]\': \'null\',
      \'[object Object]\': \'object\'
    }
    return map[toString.call(obj)] === value
  }
}

const isNumber = typeOf(\'number\')
const isString = typeOf(\'string\')
isNumber(0) // true
isString(\'a\') // true

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

函数式编程之柯里化(curry)

函数式编程:纯函数&柯里化&组合函数

函数式编程———柯里化(Currying)

函数式编程-函数的合成与柯里化

函数式编程之一柯里化

JavaScript函数式编程(纯函数柯里化以及组合函数)