Skip to content

理解「函数柯里化」 #10

@heycn

Description

@heycn

如果没有接受函数式的一整套理论,就没有必要用柯里化

柯里化 Currying:让所有函数只接受一个参数

有什么意义?

我们把一个只接受一个参数的函数叫作「单参数函数」,基于这种函数,已经衍生出非常多的理论知识,而且是关于数学方面的,跟编程关系不大

λ演算 —— lambda演算

假设一个函数只支持一个参数

那么支持接受两个参数呢?

  1. 使用对象
  2. 使用闭包

一般来说,在函数式编程中,不使用对象,而是使用闭包,因为 —— 对象是穷人的闭包
如果不理解上面这句话,可以看下我写过的一篇博客对象是穷人的闭包,闭包是穷人的对象

我们有闭包了,就不需要使用对象

使用对象

const add = ({a, b}) => a + b
add({a: 1, b: 2})

上面这种方式在函数式编程中,特别的傻

使用闭包

const add = a => b => a + b
add(1)(2)

如果没见过上面这种写法,是不是看不太习惯?或者会疑问:闭包在哪里呢?
请继续往下看:

// 以上的代码,基本等价与下面的代码
add = function(a) {
  return function(b) {
    return a + b // 这个 a 就是和这个函数形成了闭包
  }
}
add(1)(2)

但是如果只写过 JS 的人,会感觉有点不方便对不对?

没错,如果没有接受过函数式的一整套理论,你会觉得这就是在耍花招,没什么用
但是你如果接受过一系列函数式的东西,你就会觉得柯里化是非常重要的部分,如果没学过,那就没必要用柯里化

柯里化一个函数

初级

请把三参数函数 add(1,2,3) 变成 curriedAdd(1)(2)(3) 形式:

const curriedAdd =
  a =>
    b =>
      c =>
        add(a, b, c)

完毕,非常的简单。

升级

假设:

  • addTwo 接受两个参数
  • addThree 接受三个参数
  • addFour 接受四个参数
  • 如何写出一个 currify 函数,使得他们分别接受 2、3、4 次参数,如:
    • currify(addTow)(1)(2) // 3
    • currify(addThree)(1)(2)(3) // 6
    • currify(addFour)(1)(2)(3)(4) // 10

也就是说,currify 函数能将任意接受固定个参数的函数,变成单一参数的函数

const currify = (fn, params = []) => {
  return (arg) => {
    const newParams = params.concat(arg)
    if(newParams.length === fn.length) {
      return fn(...newParams)
    } else {
      return currify(fn, newParams)
    }
  }
}

const addTwo = (a, b) => a + b

再升级

我想支持 currify(addFour)(1)(2,3)(4) // 10

那么需要这么改写

const currify = (fn, params = []) => {
- return (arg) => {
+ return (...args) => {
-   const newParams = params.concat(arg)
-   if(newParams.length === fn.length) {
+   if(params.length + args.length === fn.length) {
-     return fn(...newParams)
+     return fn(...params, ...args)
    } else {
-     return currify(fn, newParams)
+     return currify(fn, [...params, ...args])
    }
  }
}

最终简化

const currify = (fn, params = []) => 
  (...args) =>
    params.length + args.length === fn.length
      ? fn(...params, ...args)
      : currify(fn, [...params, ...args])

这样,是不是对「柯里化函数」有一点点理解了呢?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions