关于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
这种行为是由于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’个参数,一旦我们有足够数量的参数,我们就会返回总和。
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
也可以用于使用固定参数来调整任何函数。
与另一个要注意add
和multiply
例子是,前3个自然数的加法和乘法是相同的。