关于 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 函数。可能有两种选择,
valueOf
财产
1.利用我们已经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
这种行为是由于valueOf
JS 引擎在需要将 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'个参数,一旦我们有足够数量的参数,我们就会返回总和。
arguments
计数的解决方案
1.使用以下代码保持传递的总参数计数,如果它达到 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
也可以用于使用固定参数来调整任何函数。
与另一个要注意add
和multiply
例子是,前 3 个自然数的加法和乘法是相同的。