Skip to content
大纲

函数柯里化 currying

  • 函数柯里化 currying
  • 偏函数 partial
  • 反柯里化 uncurrying
  • 组合函数 compose

概念

柯里化函数(Currying Function),是一种函数式编程技术。

它把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

核心思想

柯里化函数的核心思想是函数的“延迟执行”,它能够将一个函数的参数逐个收集起来,直到所有的参数都收集齐了才开始执行函数。柯里化函数使得函数的可组合性更强,同时也支持给部分参数赋值的情况下调用函数。这种方式能够使得代码更加灵活,易于重用

js
// 实现方法:用一个闭包,返回一个函数,
// 这个函数每次执行都会改写储存参数的数组,
// 当函数的参数够了之后,便会执行。
function add(a, b, c) {
  return a + b + c
}
add(1, 2, 3)

let addCurry = currying(add)
addCurry(1)(2)(3)

实现

js
// 函数柯里化
function currying(fn, ...rest) {
  // 判断参数的长度是否已经满足函数所需参数的长度
  // 如果满足,执行函数
  // 如果不满足,递归返回科里化的函数,等待参数的传入
  return fn.length <= rest.length
    ? fn(...rest)
    : currying.bind(null, fn, ...rest)

  // 等效
  // if (fn.length <= rest.length) {
  //   return fn(...rest)
  // } else {
  //   return function (...rest2) {
  //     return currying(fn, ...rest, ...rest2)
  //   }
  // }
}
js
// testing
function sumFn(a, b, c) {
  return a + b + c
}
let sum = currying(sumFn)
console.log(sum(2)(3)(5)) // 10
console.log(sum(2, 3)(5)) // 10

// 乘法
const mul = (x) => (y) => (z) => x * y * z
console.log(mul(1)(2)(3))

const mul1 = (x, y, z) => x * y * z
const mul2 = currying(mul1)
console.log(mul2(1)(2)(3))
console.log(mul2(1, 2)(3))

优点

更加灵活的函数组合

js
const add = (x) => (y) => x + y
const multiply = (x) => (y) => x * y

const addAndMultiply = (x) => add(2)(multiply(3)(x))

addAndMultiply(4) // 14

支持延迟执行

柯里化函数支持延迟执行,因为它们只会执行到所有参数都被收集完毕才会执行。这种方式能够提高函数的性能和可复用性。

js
function add(x, y) {
  return x + y
}

function currying(fn) {
  return function curried() {
    const args = Array.from(arguments)
    if (args.length >= fn.length) {
      return fn.apply(null, args)
    } else {
      return function () {
        const newArgs = Array.from(arguments)
        return curried.apply(null, args.concat(newArgs))
      }
    }
  }
}

const lazyAdd = currying(add)

const result1 = lazyAdd(1)(2) // 不执行
const result2 = lazyAdd(1, 2) // 执行

console.log(result1) // 返回一个函数
console.log(result2) // 返回结果 3

上面的例子中,我们使用柯里化函数 lazyAddadd 函数进行了包装,使其能够支持延迟执行。当我们传递一个参数时,它不会马上执行 add 函数,而是返回一个新函数去等待接收下一个参数。直到满足执行条件后再一次性执行。而当我们传递足够的参数时,它会立即执行 add 函数并返回结果。

偏函数 partial

偏函数(Partial Application)

偏函数是指使用柯里化函数对函数的一部分参数进行预处理,并返回一个函数来完成余下的工作

就是将一个 n 参的函数转换成固定 x 参的函数,剩余参数(n - x)将在下次调用全部传入。

举个例子:

js
// 计算税率
function calculateTax(rate, price) {
  return rate * price
}

// 创建一个新的函数来计算 5% 的税收(偏函数)
const calculate5PercentTax = currying(calculateTax, 0.05)
console.log(calculate5PercentTax(100)) // 5

反柯里化 uncurrying

反柯里化是指将柯里化函数还原成原函数的形式,使其能够接收多个参数

js
const add = (x) => (y) => x + y

function uncurrying(fn) {
  return function () {
    const args = Array.from(arguments)
    return args.reduce((pre, cur) => pre(cur), fn)
  }
}

const add2 = uncurrying(add)
console.log(add2(1, 2)) // 3

组合函数 compose

组合函数是指将多个函数组合到一起以达到某种效果的函数

js
function double(x) {
  return x * 2
}

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

const compose = (...fns) => {
  return function (result) {
    return fns.reduceRight((result, fn) => fn(result), result)
  }
}

const doubleAndAdd5 = compose(add(5), double)
doubleAndAdd5(2) // 9

惰性求值(Lazy Evaluation)

惰性求值是指在需要的时候才进行计算,这种方式能够提高程序的性能和响应速度

js
function add(x, y) {
  return x + y
}

// 使用惰性求值的方式将其转化成柯里化函数
// 在真正需要计算结果的时候再进行计算,从而避免不必要的计算,提高程序的性能。
const add = (x) => {
  return function (y) {
    if (y !== undefined) {
      return x + y
    } else {
      return function (y) {
        return x + y
      }
    }
  }
}

参考: