developer-plus / interview

https://interview.developer-plus.org
MIT License
9 stars 1 forks source link

如何实现一个柯里化的累加函数? #19

Open luckept opened 2 years ago

luckept commented 2 years ago

要求如下

function sum() {
}

const total = +sum(1)(2, 3)(3)
console.log("total => ", total) // total => 9

最终实现的迷惑版

function sum(...nums) {
  let acc = 0
  const fn = (...nums) => (acc = nums.reduce((acc, cur) => acc + cur, acc), fn)
  fn[Symbol.toPrimitive] = () => acc
  return fn(...nums)
}

如何理解上述最终实现

一行一行分析,第一步创建 acc = 0,其实是为了迷惑而迷惑,完全可以命名成其他诸如 sum 之类好理解的变量名,它的目的是用来闭包留存累加结果

第二步,如何理解形式为 const fn = () => (xxx, fn) 的代码

// 上述代码效果其实和这段代码一样
function fn() {
    xxx // 函数核心逻辑,此处省略
    return fn
}

而简写版只是为了利用逗号返回后一个值的特性,来省略代码

第三步,理解 acc = nums.reduce((acc, cur) => acc + cur, acc)

这里首先去掉迷惑的命名来理解,把闭包变量改名为 sum,上述代码变为 sum = nums.reduce((acc, cur) => acc + cur, sum),要注意的是这里的后一个 sum 就不再是利用逗号运算符的特性去返回 sum 了,而是每次让柯里化的闭包累加结果作为下一次 reduce 函数的初始值

第四步,理解 fn[Symbol.toPrimitive] = () => acc

因为柯里化的返回结果永远是一个函数,而我们如何去通过这个函数获取到闭包对象的值呢?这里按题目要求可以看见最终输出的时候利用了 + 号,这会触发函数的转换,而 [Symbol.toPrimitive] 则可以定义转换的规则,把闭包值作为转换后的结果(当然,这里也可以利用 valueOf 和 toString)

luckept commented 2 years ago

此处补充一个知识点,关于使用运算符来使匿名函数立即执行(PS:和本题的场景无关)

! function(x, y) { console.log(x + y) } (1, 2) // 3
+ function(x, y) { console.log(x + y) } (1, 2) // 3
- function(x, y) { console.log(x + y) } (1, 2) // 3
~ function(x, y) { console.log(x + y) } (1, 2) // 3
void function(x, y) { console.log(x + y) } (1, 2) // 3
jp-liu commented 2 years ago
function sum() {
    let sum = 0
    function t(...args){
        sum += args.reduce((a,b) => a + b, 0)
        return t
    }
    t.valueOf = function() {
        return sum
    }
    t.toString = function () {
        return sum+ ''
    }
    return t(...arguments)
}

不知是否可行

luckept commented 2 years ago

发一个可能会被同事打的迷惑版

function sum(...nums) {
  let acc = 0
  const fn = (...nums) => (acc = nums.reduce((acc, cur) => acc + cur, acc), fn)
  fn[Symbol.toPrimitive] = () => acc
  return fn(...nums)
}
Hongbusi commented 2 years ago

@luckept 可能二字去掉!

baboon-king commented 2 years ago

发个 Typescript 版 🙈

const curry = <T extends (...args: any[]) => any>(fn: T, maxArgsLen = 99) => {
  const argsLen = fn.length || maxArgsLen;
  const argList: any[] = [];
  const callback = (...args: any[]) => {
    argList.push(...args);
    if (argList.length >= argsLen || args.length === 0) {
      return fn(...argList);
    } else {
      return callback;
    }
  };
  return callback;
};

// test 1
const sum = (num1: number, num2: number, num3: number) => num1 + num2 + num3;

const curredSum = curry(sum);

console.log(curredSum(1)(2)(9));

// test 2
const sumAny = (...args: number[]) => args.reduce((pre, curr) => pre + curr);

const curredSumAny = curry(sumAny, 5);

console.log(curredSumAny(1, 2, 3)(2)());

// test 3
const curredSumAny2 = curry(sumAny, 5);
console.log(curredSumAny2(1, 2, 3)(2)(1000));

image