【问题标题】:How to curry a function across an unknown number of parameters如何跨未知数量的参数对函数进行curry
【发布时间】:2016-08-28 02:49:47
【问题描述】:

假设我有一个名为multiplyDivide的函数

如果我打电话给multiplyDivide(2)(3)(4)(6),它就相当于2 * 3 / 4 * 6

更新: 如果我事先不知道我将采用多少个参数,是否可以编写这样的函数?例如,我可以有multiplyDivide(1)(2)multiplyDivide(1)(2)(3)(4)...(n-1)(n)

【问题讨论】:

  • 提示:嵌套 5 个函数,然后在第 5 个函数内有一个块使用每个函数的参数进行操作

标签: javascript currying


【解决方案1】:

这是可能的,但您需要定义终止条件,因为这个问题本质上与编写递归函数的问题相同。该函数需要一种方法来判断它应该返回一个函数还是一个值。

如何表明对价值观的需求取决于您。一种方法是检查是否传递了参数:

// Using add instead of multiplyDivide to simplify example:

function add (num) {
    function adder (n) {
        if (n !== undefined) {
            num += n;
            return adder;
        }
        else { // terminate
            return num;
        }
    }
    return adder;
}

现在你可以这样做了:

var sum = add(1)(2)(3)(4)();

否则它会返回一个你可以继续调用的函数:

var x = add(1)(2)(3)(4);
x = x(5)(6)(7);
x = x(8)(9)(10);

var sum = x();

由于在 js 中函数是对象,你也可以将 value getter 实现为静态方法。它不是纯粹的功能性,而是使“API”更加明确和易于阅读:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.value = function(){
        return num
    };
    return adder;
}

你可以这样做:

var sum = add(1)(2)(3)(4).value();

您甚至可以通过覆盖内置的 .valueOf().toString() 方法来获得幻想:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.valueOf = function(){
        return num
    };
    adder.toString = function(){
        return '' + num
    };
    return adder;
}

你可以这样做:

var sum = add(1)(2)(3)(4) + 5; // results in 15
var txt = add(1)(2)(3)(4) + "hello"; // results in "10hello"

这里的关键是你需要一种方法来告诉函数停止返回函数。

【讨论】:

  • 我认为这应该是公认的答案。它实际上回答了这个问题。接受的答案甚至不使用柯里化。我有同样的问题,这篇文章给了我正在寻找的答案。谢谢!
【解决方案2】:

使用函数式方法,您可以创建一个为另一个函数“curry”参数的函数。您将需要一种方法来告诉函数返回值,因此在这种情况下,调用函数而不传递任何参数将返回结果:

function curry(fn, ...values) {
    return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

这个函数很酷的一点是你可以传递多个参数和/或继续调用函数(1)(2, 3, 4)(5)

这里有几个例子:

function curry(fn, ...values) {
  return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

let x = curry(multiplyDivide)(2)(3, 4)(6)();
console.log(x);

let y = curry(multiplyDivide)(5, 4, 2)(3);
y = y(3, 5)(1)();
console.log(y);

当然,这个例子确实暗示了只是简单地重载 multiplyDivide 函数并在你准备好时将你的值传递给它:

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

const values = [5, 4, 2, 3, 3];
values.push(5, 1);

console.log(multiplyDivide(...values));

【讨论】:

    【解决方案3】:

    你的意思是这样的吗?

    var multiplyDivide = function(first){
      return function(second){
        return function(third){
          return function(forth){
            return first * second / third * forth;
          }
        }
      }
    }
    //should be 6
    console.log(multiplyDivide(2)(4)(4)(3));
    

    【讨论】:

    • @NilesTurner 抱歉,我不是很清楚。我已经知道如何使用固定数量的参数来做到这一点,但我不知道它是否是可变的。
    • @AlanH 这更有意义,但是如果输入的数量是可变的,那么操作模式是什么?你建议a * b / c * d,它只是为每个额外的输入翻转操作数吗?所以 5 个输入将是 a* b / c * d / e?
    • 是的,它只是交替
    【解决方案4】:

    这其实是一个很好的问题……

    柯里化是函数式编程语言最基本的方面之一,您可以在其中传递和返回函数。 JS在某种程度上是一种函数式编程语言,应该可以执行这个操作。

    因此,就像您问题中的那个一样,出于这个或那个原因,您可能想要使用无限多的参数函数。

    好的,让你的功能成为

    function multiplyDivide (n,m,o,p){
      return n * m / o * p;
    }
    

    那么实现实用程序curry 函数以获取任何给定函数的柯里化版本的方法如下;

    var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),
    

    让我们看看它的实际效果

    function multiplyDivide (n,m,o,p){
          return n * m / o * p;
        }
    
    var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),
        f     = curry(multiplyDivide);
     
     res1 = f(4,5,2,10),
     res2 = f(4)(5,2,10),
     res3 = f(4)(5)(2,10),
     res4 = f(4)(5)(2)(10),
     res5 = f(4,5)(2,10),
     res6 = f(4,5)(2)(10),
     res7 = f(4,5,2)(10),
     res8 = f(4,5)(2,10);
    console.log(res1,res2,res3,res4,res5,res6,res7,res8);

    可能有更简单的方法,但这是我能想到的。

    【讨论】:

    • 你能解释一下下面的代码 - var curry = f => f.length 吗? (...a) => curry(f.bind(f,...a)) : f(), f = curry(multiplyDivide);
    • @BHUVNESH KUMAR 你可以看看Partially Applied Functions use caseofbind
    【解决方案5】:

    function addValues(a, b) {
       if(b!=undefined)
        return a + b;
    
       return function(b) {
           return a + b;
       }
    }
    let output1 = addValues(2)(4);  // 6
    let output2 = addValues(2,1);   // 3
    console.log(output1);
    console.log(output2)

    【讨论】:

      【解决方案6】:
      function multiply(a) {
         return function(b) {
             return b ? multiply(a*b) : a;
         }
      }
      
      console.log(multiply(2)());
      console.log(multiply(2)(3)());
      console.log(multiply(2)(3)(4)());
      console.log(multiply(2)(3)(4)(5)());
      

      【讨论】:

        【解决方案7】:
        function add() {
        let args = [...arguments];
          function addAll(){
           let args1 = [...arguments];
           return add(...args, ...args1);
         }
        
        let total = args.reduce((total, value) => total+value);
        addAll.value = total;
        return addAll;
        }
        
        console.log(add(2)(3)(4).value); 
        console.log(add(2)(3).value); 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-08-13
          • 1970-01-01
          • 1970-01-01
          • 2020-07-22
          • 2016-07-17
          • 1970-01-01
          相关资源
          最近更新 更多