简体   繁体   English

柯里化一个需要无限 arguments 的 function

[英]Currying a function that takes infinite arguments

Using ES5, how do you curry a function that takes infinite arguments.使用 ES5,你如何咖喱一个需要无限 arguments 的 function。

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

The function above takes only three arguments but we want our curried version to be able to take infinite arguments.上面的 function 只需要三个 arguments 但我们希望我们的咖喱版本能够使用无限的 arguments。

Hence, of all the following test cases should pass:因此,以下所有测试用例都应该通过:

var test = add(1);

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

Here is the solution that I came up with:这是我想出的解决方案:

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;
    }
}

However, I have been told that it's not very “functional” in style.但是,有人告诉我,它的风格不是很“实用”。

Method 1: Using partial方法一:使用partial

A simple solution would be to use partial as follows:一个简单的解决方案是使用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; }

Method 2: Single Level Currying方法 2:单级柯里化

If you only want one level of currying then this is what I would do:如果你只想要一个级别的柯里化,那么这就是我会做的:

 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; }; }

Method 3: Infinite Level Currying方法 3:无限级柯里化

Now, here's a more general solution with infinite levels of currying:现在,这是一个更通用的解决方案,具有无限级别的柯里化:

 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; }

A running total is the intermediate result of a summation .运行总计求和的中间结果。 The running function returns another function which can be treated as a number (eg you can do 2 * running(21) ). running函数返回另一个可以被视为数字的函数(例如,您可以执行2 * running(21) )。 However, because it's also a function you can apply it (eg you can do running(21)(21) ).但是,因为它也是一个函数,所以您可以应用它(例如,您可以执行running(21)(21) )。 It works because JavaScript uses the valueOf method to automatically coerce objects into primitives.它之所以有效,是因为 JavaScript 使用valueOf方法自动将对象强制转换为原语。

Furthermore, the function produced by running is recursively curried allowing you to apply it as many times to as many arguments as you wish.此外, 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; }

The only thing you need to be aware of is that sometimes you need to manually coerce the result of running into a number by either applying the unary plus operator to it or calling its valueOf method directly.您唯一需要注意的是,有时您需要通过将一元加运算符应用于数字或直接调用其valueOf方法来手动强制running结果。

Part of the reason your add function is not very "functional" is because it is attempting to do more than just add up numbers passed to it.您的add函数不是很“实用”的部分原因是它试图做的不仅仅是将传递给它的数字相加。 It would be confusing for other developers to look at your code, see an add function, and when they call it, get a function returned to them instead of the sum.如果其他开发人员查看您的代码,看到一个add函数,并且当他们调用它时,得到一个返回给他们的函数而不是 sum,这会让人感到困惑。

For example:例如:

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

The functional approach功能方法

The functional approach would be to create a function that allows you to curry any other functions, and simplify your add function :函数式方法是创建一个函数,允许您对任何其他函数进行柯里化,并简化您的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;
    });
}

Now, if you want to curry this function, you would just do:现在,如果你想 curry 这个函数,你可以这样做:

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

If I still want to add 1, 2, 3 up I can just do:如果我仍然想添加1, 2, 3我可以这样做:

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

Continuing the functional approach继续功能方法

This code is now becoming reusable from everywhere.这段代码现在可以在任何地方重用。

You can use that curry function to make other curried function references without any additional hassle.您可以使用该 curry 函数来创建其他 curry 函数引用,而无需任何额外的麻烦。

Sticking with the math theme, lets say we had a multiply function that multiplied all numbers passed to it:坚持数学主题,假设我们有一个将传递给它的所有数字相乘的乘法函数:

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

This functional currying approach allows you take that approach to any function, not just mathematical ones.这种函数式柯里化方法允许您将这种方法应用于任何函数,而不仅仅是数学函数。 Although the supplied curry function does not support all edge cases, it offers a functional, simple solution to your problem that can easily be built upon.尽管提供的curry函数不支持所有边缘情况,但它为您的问题提供了一个功能性、简单的解决方案,可以轻松构建。

Similar to the above problem.类似于上面的问题。 Sum of nth level curry by recursion通过递归计算第 n 级 curry 的总和

Trick: To stop the recursion I'm passing last () as blank**技巧:为了停止递归,我将 last () 传递为空白**

 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)())

There is more generic approach by defining a curry function that takes minimum number of arguments when it evaluates the inner function.通过定义一个在计算内部函数时采用最少数量的参数的 curry 函数,还有更通用的方法。 Let me use ES6 first (ES5 later), since it makes it more transparent:让我先使用 ES6(稍后使用 ES5),因为它更透明:

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

Then define a function that sums all arguments:然后定义一个对所有参数求和的函数:

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

then we can curry it, telling that it should wait until at least 2 arguments:然后我们可以对它进行 curry,告诉它应该等到至少 2 个参数:

var add = curry(2, sum);

Then it all fits into place:然后一切就绪:

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

You can even skip creating add by providing the first argument(s):您甚至可以通过提供第一个参数来跳过创建add

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

ES5 version of curry is not as pretty for the lack of ... operator:由于缺少...运算符,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;
    });
};

The rest is the same...其余的都是一样的...

Note: If efficiency is a concern, you may not want to use slice on arguments , but copy it to a new array explicitly.注意:如果效率是一个问题,您可能不想在arguments上使用slice ,而是将其显式复制到一个新数组中。

Bit late in this game, but here is my two cents.这场比赛有点晚了,但这是我的两分钱。 Basically this exploits the fact that functions are also objects in JavaScript.基本上,这利用了函数也是 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;
}

Infinite sum with currying, you can pass a single parameter or multiple up-to infinite:使用currying的无限和,您可以传递单个参数或多个直到无限:

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)
    }
}

This is my solution for single level currying这是我的单级柯里化解决方案

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

and the solution for infinite level currying以及无限级柯里化的解决方案

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

Simple solution简单的解决方案

 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM