柯里化一个带有无限参数的函数

问题描述 投票:0回答:8

使用 ES5,如何柯里化一个带有无限参数的函数。

function add(a, b, c) {
    return a + b + c;
}

上面的函数只接受三个参数,但我们希望我们的柯里化版本能够接受无限参数。

因此,以下所有测试用例都应该通过:

var test = add(1);

test(2);     //should return 3
test(2,3);   //should return 6
test(4,5,6); //should return 16

这是我想出的解决方案:

function add(a, b, c) {
    var args = Array.prototype.slice.call(arguments);

    return function () {
        var secondArgs = Array.prototype.slice.call(arguments);
        var totalArguments = secondArgs.concat(args);

        var sum = 0;

        for (i = 0; i < totalArguments.length; i++) {
            sum += totalArguments[0];
        }

        return sum;
    }
}

但是,有人告诉我,它的风格不太“实用”。

javascript functional-programming
8个回答
10
投票

与上面的问题类似。通过递归计算第 n 层柯里化之和

技巧:为了停止递归,我将最后一个 () 作为空白传递**

function sum(num1) {
        return (num2) => {
            if(!num2) {
                return num1;
            }
            return sum(num1 + num2);
        }
    }
console.log('Sum :', sum(1)(2)(3)(4)(5)(6)(7)(8)())


8
投票

您的

add
函数不是很“实用”,部分原因是它试图做的不仅仅是将传递给它的数字相加。如果其他开发人员查看您的代码,看到一个
add
函数,并且当他们调用它时,得到一个返回给他们的函数而不是求和,这会让他们感到困惑。

例如:

//Using your add function, I'm expecting 6
add(1,2,3) //Returns another function = confusing!

功能性方法

函数式方法是创建一个函数,允许您柯里化任何其他函数,并简化您的

add function

function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);

    return function () {
        return fn.apply(this, args.concat(
                Array.prototype.slice.call(arguments, 0)
        ));
    }
}

function add() {
    var args = Array.prototype.slice.call(arguments);

    return args.reduce(function (previousValue, currentValue) {
        return previousValue + currentValue;
    });
}

现在,如果你想柯里化这个函数,你只需这样做:

var curry1 = curry(add, 1);
console.log(
        curry1(2), // Logs 3
        curry1(2, 3), // Logs 6
        curry1(4, 5, 6) // Logs 16
);

//You can do this with as many arguments as you want
var curry15 = curry(add, 1,2,3,4,5);
console.log(curry15(6,7,8,9)); // Logs 45

如果我还想添加

1, 2, 3
我可以这样做:

add(1,2,3) //Returns 6, AWESOME!

继续功能性方法

此代码现在可以在任何地方重复使用。

您可以使用该柯里化函数来创建其他柯里化函数引用,而无需任何额外的麻烦。

坚持数学主题,假设我们有一个乘法函数,可以将传递给它的所有数字相乘:

function multiply() {
    var args = Array.prototype.slice.call(arguments);

    return args.reduce(function (previousValue, currentValue) {
        return previousValue * currentValue;
    });
}

multiply(2,4,8) // Returns 64

var curryMultiply2 = curry(multiply, 2);
curryMultiply2(4,8) // Returns 64

这种函数柯里化方法允许您将该方法应用于任何函数,而不仅仅是数学函数。尽管提供的

curry
函数不支持所有边缘情况,但它为您的问题提供了一个实用且简单的解决方案,可以轻松构建。


7
投票

方法一:使用
partial

一个简单的解决方案是使用

partial
,如下所示:

Function.prototype.partial = function () {
    var args = Array.prototype.concat.apply([null], arguments);
    return Function.prototype.bind.apply(this, args);
};

var test = add.partial(1);

alert(test(2));     // 3
alert(test(2,3));   // 6
alert(test(4,5,6)); // 16

function add() {
    var sum = 0;
    var length = arguments.length;
    for (var i = 0; i < length; i++)
        sum += arguments[i];
    return sum;
}

方法 2:单层柯里化

如果您只想要一级柯里化,那么我会这样做:

var test = add(1);

alert(test(2));     // 3
alert(test(2,3));   // 6
alert(test(4,5,6)); // 16

function add() {
    var runningTotal = 0;
    var length = arguments.length;
    for (var i = 0; i < length; i++)
        runningTotal += arguments[i];

    return function () {
        var sum = runningTotal;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return sum;
    };
}

方法3:无限级柯里化

现在,这是一个更通用的解决方案,具有无限级别的柯里化:

var add = running(0);

var test = add(1);

alert(+test(2));     // 3
alert(+test(2,3));   // 6
alert(+test(4,5,6)); // 16

function running(total) {
    var summation = function () {
        var sum = total;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return running(sum);
    }

    summation.valueOf = function () {
        return total;
    };

    return summation;
}

运行总计求和的中间结果。

running
函数返回另一个可以被视为数字的函数(例如,您可以执行
2 * running(21)
)。但是,因为它也是一个函数,所以您可以应用它(例如,您可以执行
running(21)(21)
)。它之所以有效,是因为 JavaScript 使用
valueOf
方法自动将对象强制转换为基元。

此外,

running
生成的函数是递归柯里化的,允许您将其多次应用到任意数量的参数。

var resultA = running(0);
var resultB = resultA(1,2);
var resultC = resultB(3,4,5);
var resultD = resultC(6,7,8,9);

alert(resultD + resultD(10)); // 100

function running(total) {
    var summation = function () {
        var sum = total;
        var length = arguments.length;
        for (var i = 0; i < length; i++)
            sum += arguments[i];
        return running(sum);
    }

    summation.valueOf = function () {
        return total;
    };

    return summation;
}

您唯一需要注意的是,有时您需要通过应用

一元加运算符
或直接调用其 running 方法来手动将
valueOf
的结果强制转换为数字。


1
投票

柯里化的无限和,您可以传递单个参数或多个直至无限:

function adding(...arg) {

    return function clousureReturn(...arg1) {
        if (!arguments.length) {
            let finalArr = [...arg, ...arg1];
            let total = finalArr.reduce((sum, ele) => sum + ele);
            return total;
        }

        return adding(...arg, ...arg1)
    }
}

1
投票

这是单参数无限柯里化的解决方案

function sum(x) {
  return function(y) {
    if (y !== undefined) {
      return sum(x + y);
    }
    return x;
  }
}

sum(1)(2)(3)(4)() //10

无限参数柯里化的解决方案

let sum = function (...args1) { let total =args1.reduce((total,num) => total + num,0) return function(...args2) { if(args2.length!== 0) { let total2 = args2.reduce((total,num)=>total + num,0); return sum(total,total2); } return total; }; }; console.log(sum(2,3,4)(2,3)(1)()); // 15

还有一个

简单可变参数函数

function sum() { let args = [...arguments]; let total = args.reduce((total,num) => total + num,0); return total; } console.log(sum(1,2,3,4)) // 10
    

0
投票
还有更通用的方法,即定义一个柯里函数,该函数在计算内部函数时采用最少数量的参数。让我先使用 ES6(稍后使用 ES5),因为它更透明:

var curry = (n, f, ...a) => a.length >= n ? f(...a) : (...ia) => curry(n, f, ...[...a, ...ia]);

然后定义一个对所有参数求和的函数:

var sum = (...args) => args.reduce((a, b) => a + b);

然后我们可以柯里化它,告诉它应该等待至少 2 个参数:

var add = curry(2, sum);

然后一切就位了:

add(1, 2, 3) // returns 6 var add1 = add(1); add1(2) // returns 3 add1(2,3) // returns 6 add1(4,5,6) // returns 16

您甚至可以通过提供第一个参数来跳过创建

add

var add1 = curry(2, sum, 1);

ES5 版本的 curry 没有那么漂亮,因为缺少

...

 运算符:

function curry(n, f) { var a = [].slice.call(arguments, 2); return a.length >= n ? f.apply(null, a) : function () { var ia = [].slice.call(arguments); return curry.apply(null, [n, f].concat(a).concat(ia)); }; } function sum() { return [].slice.call(arguments).reduce(function (a, b) { return a + b; }); };

其余都一样...

注意:如果考虑效率,您可能不想在

slice

 上使用 
arguments
,而是显式将其复制到新数组。


0
投票
这场比赛有点晚了,但这是我的两分钱。基本上,这利用了 JavaScript 中函数也是对象的事实。

function add(x) { if (x === undefined) { return add.numbers.reduce((acc, elem) => acc + elem, 0); } else { if (add.numbers) { add.numbers.push(x); } else { add.numbers = [x]; } } return add; }
    

0
投票
简单的解决方案

const add = (one) => { // one: Parameter passed in test return (...args) => { // args: Array with all the parameters passed in test return one + args.reduce((sum, i) => sum + i, 0) // using reduce for doing sum } } var test = add(1); console.log(test(2)); //should return 3 console.log(test(2, 3)); //should return 6 console.log(test(4, 5, 6)); //should return 16

© www.soinside.com 2019 - 2024. All rights reserved.