高阶函数-柯里化

514 阅读3分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

第一次接触柯里化函数的概念是在多年前一次面试过程中,当时并不知道柯里化的概念,暴力使用闭包将题目解答😄,暴力解题之后总觉得解决方案不妥,随之在网上进行搜索查阅,面试题就是一道基础且典型的柯里化题目

柯里化

什么是柯里化?

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。

在直觉上,柯里化声称“如果你固定某些参数,你将得到接受余下参数的一个函数”。所以对于有两个变量的函数yx,如果固定了 y = 2,则得到有一个变量的函数 2x。

在不使用柯里化时,实现一个三数累加的功能

const add = (a, b, c) => {
  return a + b + c;
};

调用方式会比较单一,需要一次性将参数全部传递方可得到期望的结果

add(1, 2, 3)

若是函数需要的参数需要分次传递,那么此时的调用方式便不符合期望,这个时候便可以体现柯里化的一个优势,可以将一个函数包装为可多次调用执行的函数,直到达到期望参数才会返回结果

首先实现一个简单版本的柯里化函数

const currying = (fn, ...outParams) => {
  // 获取 fn 函数需要的参数个数
  const paramsLen = fn.length;

  return (...args) => {
    // 收集全部参数
    let params = [...outParams, ...args];
    // 若参数没有达到 fn 需要的参数,继续收集参数
    if (params.length < paramsLen) {
      return currying(fn, ...params);
    }

    return fn(...params);
  };
};

使用的方式也很简单

let newAdd = currying(add);
newAdd(1, 2)(3); // 6
newAdd(2, 4, 6); // 12

优势

柯里化相对普通的函数具有一些优势:延迟运行、参数复用

延迟运行

上面的三数累加可以很好的体现柯里化函数的延迟运行的优化,可以等到收集完需要的参数才执行函数获取结果

参数复用

现在需要编写一个判断数据类型的函数

/** 
 * type: 类型 - [object Array]、[object Number]等
 * source: 数据源
*/
const judgeType = (type, source) => {
  return Object.prototype.toString.call(source) === type
}

使用时:判断是不是Array类型

judgeType('[object Array]', []) // true
judgeType('[object Array]', 123) // false

咋一看没啥毛病,但是当使用judgeType函数时,却发现存在一些问题

不方便:每一次调用judgeType函数时,不仅仅需要传递数据源,还需要传递数据类型,大大的增加使用的不方便

不稳定:在传递type时,会发现[object Array]、[object Number]等较为长且繁琐,很容易出现不小心写错参数导致结果不稳定

配合柯里化使用,将judgeType进行柯里化:判断是不是Array类型

const isArray = currying(judgeType, '[object Array]')

**'[object Array]'**即时被复用的参数,只需要传递一次,此后的调用都会使用本次的参数

得到isArray函数之后,每一次调用只需要传递数据源即可

isArray([]) // true
isArray('') // false

可以看到处理之后函数的调用更加的方便、更加的语义化且更稳定