如何正确柯里化这个函数?

How do I curry this function correctly?

我尝试使用 curryDecorate

柯里化函数 add

const curryDecorate = (fn, ...args) => {
  const curried = (...newArgs) => {
    args = args.concat(newArgs)
    if (args.length < fn.length) {
      return curried
    }
    return fn(...args)
  }
  return curried
}

const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)

console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6

sum(1, 2)(3)为运行时,我得到

sum(...) is not a function

运行单独设置底部3行时正常, 但一起抛出该错误。

您正在使用 sum 变量两次进行柯里化,这只会保存一组参数。

sum(1)(2)(3) // args: 1, 2, 3
sum(1, 2) // persists, so args: 1, 2, 3, 1, 2

这解决了您的问题:

const curryDecorate = (fn, ...args) => {
  let pass=[...args];
  const curried = (...newArgs) => {
    pass= pass.concat(newArgs)
    if (pass.length < fn.length) {
      return curried
    }
    const val = fn(...pass);
    pass=[...args]
    return val;
  }
  return curried
}

const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)

console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6

const curryDecorate = (fn, ...args) => {
  const curried = (...newArgs) => {
    args = args.concat(newArgs)
    console.log('fn len', fn.length, args.length, args.length < fn.length)
    if (args.length < fn.length){
      return curried
    }
    const currArgs = [...args]
    args = []
    return fn(...currArgs)
  }
  return curried
}

const add = (a, b, c) => console.log(a + b + c)
const sum = curryDecorate(add)

sum(1)(2)(3)
sum(1)(2)(3)
sum(1)(2)(3)
sum(1)(2)(3)
sum(1)(2)(3)
  // 6

问题是您目前只有一个 args 的实例,它在修饰的柯里化函数的所有版本之间共享。这意味着一旦你获得足够的参数 once 任何进一步的 curried 调用将直接进入 return fn(...args) 并且因此再次执行将失败:

             console.log(sum (1) (2) (3))
//                           ^^^ ^^^ ^^^
//                            |   |   |
//args = [1] -----------------+   |   |
//args = [1, 2] ------------------+   |
//args = [1, 2, 3] -------------------+

             console.log(sum (1, 2) (3))
//                           ^^^^^^
//                             |
//args = [1, 2, 3, 1, 2] ------+ 

在第四次调用 sum(1, 2) 时,args 数组包含 [1, 2, 3, 1, 2],比 fn.length,因此 sum(1, 2) 将执行函数和 return 6。下一个 (3) 将尝试将数字作为函数执行并失败:

const curryDecorate = (fn, ...args) => {
  const curried = (...newArgs) => {
    args = args.concat(newArgs)
    if (args.length < fn.length) {
      return curried
    }
    return fn(...args)
  }
  return curried
}

const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)

console.log(sum(1)(2)(3)) // 6
const result = sum(1, 2);
console.log(result)       // 6
console.log(result(3))    // error

您可以利用 curryDecorate 已经将 args 作为参数这一事实来避免此错误。如果 curried 调用不满足参数要求,您可以使用到目前为止的所有参数再次调用 curryDecorate。这样你就可以得到单独的函数对象,每个函数对象都有一个单独的部分填充的参数列表:

const curryDecorate = (fn, ...args) => {
  const curried = (...newArgs) => {
    //collect all into newArgs
    newArgs = args.concat(newArgs)
    if (newArgs.length < fn.length) {
      //call curryDecorate with arguments so far
      return curryDecorate(fn, ...newArgs)
    }
    //execute with all collected args
    return fn(...newArgs)
  }
  return curried
}

const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)

console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6

console.log("-----");

const sum1 = sum(1);

const sum14 = sum1(4);
const sum15 = sum1(5);

const sum144 = sum14(4);
const sum145 = sum14(5);
const sum155 = sum15(5);
const sum156 = sum15(6);

console.log(sum144);
console.log(sum145);
console.log(sum155);
console.log(sum156);
.as-console-wrapper { max-height: 100% !important; }

再往前走一步,如果 curryDecorate 无论如何都要用参数调用,执行或不执行函数的逻辑可以移到那里,事情会简化一点:

const curryDecorate = (fn, ...args) => {
  if (args.length >= fn.length)
    return fn(...args);
    
  return (...newArgs) =>
    curryDecorate(fn, ...args, ...newArgs);
}

const curryDecorate = (fn, ...args) => {
  if (args.length >= fn.length)
    return fn(...args);
    
  return (...newArgs) =>
    curryDecorate(fn, ...args, ...newArgs);
}

const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)

console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6

console.log("-----");

const sum1 = sum(1);

const sum14 = sum1(4);
const sum15 = sum1(5);

const sum144 = sum14(4);
const sum145 = sum14(5);
const sum155 = sum15(5);
const sum156 = sum15(6);

console.log(sum144);
console.log(sum145);
console.log(sum155);
console.log(sum156);
.as-console-wrapper { max-height: 100% !important; }