关于 Add(2)(3)问题

首先,add(2)(3)用 JavaScript 实现

首先,如果我们做一个简单的分析,我们可以简单地说这不仅仅是针对 JavaScript 的问题,而是可以用任何具有 First Class 功能的语言实现。

当该语言中的函数被视为与任何其他变量一样时,编程语言被称为具有第一类函数。例如,在这种语言中,函数可以作为参数传递给其他函数,可以由另一个函数返回,并可以作为值赋值给变量。

现在我们只需要创建一个返回另一个函数的函数,该函数反过来会给出总和。而已。

在继续之前,如果您是第一次遇到此问题,请自行尝试此问题。

解,

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

也可以使用箭头函数在 ES6 中实现,

const add = x => y => x + y

这个问题只不过currying是 JS 中的概念。

什么是 currying?

Currying 是一种将具有多个参数的函数评估为具有单个/多个参数的函数序列的技术。在上述问题中,我们只需打开了add(2,3)进入add(2)(3)

你可以从这篇文章中深入讨论

add(2)(3)问题的变体

也可以看到这种干扰问题的变化很少

add(2)(3)(4)...,用于无数个参数

嗯,我们知道如何处理求和和返回函数(以及闭包)但我们不确定何时停止,这意味着,主函数何时返回结果以及何时返回另一个 curried 函数。可能有两种选择,

1.利用valueOf财产

我们已经ToPrimitive在本博客中看到了 JS 引擎如何处理操作。考虑到这一事实,如果我们返回一个对象(或函数),其valueOf属性返回到目前为止计算的结果,我们将能够区分返回函数以进行进一步求和到目前为止的求和结果。让我们来看看,

function add(x) {
  let sum = x
  function resultFn(y) {
    sum += y
    return resultFn
  }
  resultFn.valueOf = function() {
    return sum
  }
  return resultFn
}

以下执行可行,

> 5 + add(2)(3) //output: 10
> console.log(add(2)(3)(4)==9) //output: true
> add(3)(4)(5).valueOf() //output: 12

另一方面,例如,这将无法工作或意外地在少数地方出现

> add(3)(4)(5) //return function
> console.log(add(3)(4)(5)) // output: function
> console.log(add(3)(4)(5)===12)// output: false

这种行为是由于valueOfJS 引擎在需要将 add(2)(3)(4)的结果转换为基本类型时将调用该属性。上述所有给出正确结果的语句都是由于 JS 引擎试图将结果转换为原始值。

2.明确要求财产

另一种方法可能是,我们遵循一个约定,函数的使用者应该在结果中显式调用属性来获得求和。此解决方案与使用的解决方案非常相似valueOf,但不会发生隐式转换。像这样的东西,

function add(x) {
  let sum = x
  return function resultFn(y) {
    sum += y
    resultFn.result = sum
    return resultFn
  }
}

消费将是,

> add(3)(4)(5).result //output: 12
> var t = add(3)(4);
> t.result //output: 7
> t(5).result //output: 12

如果必须实现这种类型的东西,它应该是通过模块/类,而不是简单的函数来模拟行为。

3.显式调用函数,没有最终结果的参数

还可以设计函数以在没有参数的情况下调用函数时返回结果求和。如果传递参数,它将继续将这些数字添加到先前的结果。

function add(x) {
  let sum = x
  return function resultFn(y) {
    if (arguments.length) {
      //not relying on falsy value
      sum += y
      return resultFn
    }
    return sum
  }
}

这可以用以下方式,

> add(2)(3)() //output: 5
> var t = add(3)(4)(5)
> t() //output: 12

add(2)(3)(4) and add(2,3,4) 在同一功能中使用。

这是另一个方差,其中同一函数应满足用例add(2)(3)(4)和/ add(2,3,4)或任何组合。因此,单个函数应满足以下情况,

  • 添加(2)(3)(4)
  • 添加(2,3,4)
  • 添加(2)(3,4)
  • 添加(2,3)(4)

对于这种类型,让我们考虑固定的'n'个参数(在我们的例子中,n = 3)。如果我们需要使用可变数量的参数来实现这一点,我们可以通过上述问题的解决方案来解决这些问题。这里的技巧是跟踪'n'个参数,一旦我们有足够数量的参数,我们就会返回总和。

1.使用arguments计数的解决方案

以下代码保持传递的总参数计数,如果它达到 3,则给出结果总和

function add() {
  let args = [].slice.apply(arguments)
  function resultFn() {
    args = args.concat([].slice.apply(arguments))
    if (args.length >= 3) {
      return args.slice(0, 3).reduce(function(acc, next) {
        return acc + next
      }, 0) //will only sum first 3 arguments
    }
    return resultFn
  }
  return resultFn()
}

样品用法,

> add(2)(3)(4) //output: 9
> add(2,3,4) //output: 9
> add(2)(3,4) //output: 9
> add(2,3)(4) //output: 9

2.固定参数函数的通用解决方案

这里的方法是创建一个更高阶的函数,它将获取该函数必须的函数和参数数量 - 在我们的例子中为 add(2,3,4)。除非总收集的参数与传递函数的预期参数相同,否则此函数将跟踪参数。

function fixCurry(fn, totalArgs) {
  totalArgs = totalArgs || fn.length
  return function recursor() {
    return arguments.length < totalArgs
      ? recursor.bind(this, ...arguments)
      : fn.call(this, ...arguments)
  }
}

上面的函数接受一个函数 - fn并且可选地totalArgs在调用之前是必需的fn。如果totalArgs没有传递,它将依赖于函数签名并使用属性fn.length,该属性是已定义函数的参数个数。 totalArgs可用于函数 - fn其实现本身依赖arguments于其签名中未定义参数。 fixCurry返回一个不断向函数添加(通过bind)参数的函数,如果达到阈值,它只调用所有调用之间收集的所有参数的函数。

让我们看看样品的用法,

> var add = fixCurry((a,b,c)=>a+b+c); //fn = summation function
> console.log(add(1,2, 3))  // output: 6
> console.log(add(1)(2,3)) // output: 6
> console.log(add(1)(3)(2)) // output: 6
> console.log(add(1,2)(3)) // output: 6

同样适用于乘法(或任何其他 curried 函数),

> var multiply = fixCurry((a,b,c)=>a*b*c); //fn = multiplication function
> console.log(multiply(1,2, 3))  // output: 6
> console.log(multiply(1)(2,3)) // output: 6
> console.log(multiply(1)(3)(2)) // output: 6
> console.log(multiply(1,2)(3)) // output: 6

fixCurry也可以用于使用固定参数来调整任何函数。

与另一个要注意addmultiply例子是,前 3 个自然数的加法和乘法是相同的。

上次更新: 1/4/2020, 8:02:19 PM