繁体   English   中英

javascript函数式编程 - 添加(a)(b)(c)

[英]Functional programming in javascript - add(a)(b)(c)

我试图围绕js中的函数式编程。

我理解add(3)(5)将是:

function add(x) {
    return function(y) {
        return x + y;
    };
}

如何更改此功能以便添加(3)(5)(7)(8)返回23或添加(1)(2)(3)返回6?

如果不修改add的定义,则需要链接要add的调用(add(add(add(3)(5))(7)))(8)

澄清一下,这个表达式分解为:

add(3) //returns a function that adds 3
add(3)(5) //returns 8

add(add(3)(5)) // returns a function that adds 8
(add(add(3)(5)))(7) // returns 15

add ((add(add(3)(5)))(7)) //returns function that adds 15
(add(add(add(3)(5))(7)))(8) //returns 23

在@zerkms提到的时候进一步细分(并将我们的函数定义分配给变量),我们可以看到add的链接是如何工作的:

var add3 = add(3) //returns a function that adds 3
add3(5) //returns 8

var add8 = add(add3(5)) // returns a function that adds 8
add8(7) // returns 15

var add15 = add(add8(7)) //returns function that adds 15
add15(8) //returns 23

通过链接,我们将添加上一次add调用的结果。

这意味着如果add(3)返回一个添加3的函数,然后你向该数字添加5,那么你可以将该值8传递给另一个要add调用,以使另一个函数为它的参数添加8。

你可以做这样的事情。

function add(x) {
  return function(y) {
    if (y) {
      return add(x+y);
    }
    return x;
  };
}

在这里,您可以根据需要多次拨打电话。

add(4)();
add(4)(5)(9)();
add(1)(2)(3)....(n)();

示例链接

怎么样:

function add(x) {    
    return function(y) {
        return y == 0 ? 
               x + y :
               add(x + y);
     };
}

add(3)(5)(7)(8)(0) // ==> 23
add(1)(2)(3)(0)    // ==> 6

这里的技巧是它知道何时通过参数的值返回函数或答案。 “停止”值可能是真正的任何东西,但在我的例子中它是0触发答案。

TL; DR

数字不能作为函数调用。 因此,使用复合模式并返回在每次迭代时具有累积结果的函数。 以下是从工厂实现add测试和示例用法的一种方法: http//jsbin.com/qaqiqu/edit?js,console

细节:

var factory = (function (){
  "use strict";

  var reduceFactory = function (options) {
    var defaultReduce = function (x, y) { return x + y; },//add
        noLog = function () {};

        function store(x) {
          if (options && options.log) {
            options.log(x, options.acc, options);
          }
          options.acc = options.f(options.acc, x);
          store.result = options.acc;
          return store;
        }
        //default options
        options = typeof options !== 'undefined' ? options : {};
        options.f = typeof options.f !== 'undefined' ? options.f : defaultReduce;
        options.acc = typeof options.acc !== 'undefined' ? options.acc : 0;
        options.log = typeof options.log !== 'undefined' ? options.log : noLog;

        return store;
      };

  return reduceFactory;
}());

//example usage
(function (f) {
  var add = f(),
      add1 = f(),
      add2 = f(),
      add3 = f(),
      add4 = f(),
      clear = function(f) {
        return f(-f.result);
      };
  //how to use a single function
  console.log('add(3)(5)(7)(8).result = ' + add(3)(5)(7)(8).result);
  clear(add);
  console.log('add(1)(2)(3).result = ' + add(1)(2)(3).result);

  //how to use factory functions
  console.log('add1(3)(5)(7)(8).result = ' + add1(3)(5)(7)(8).result);
  console.log('add2(1)(2)(3).result = ' + add2(1)(2)(3).result);

  //factory functions can continue to grow as needed  
  add3(3)(5);
  add3(7)(8);
  console.log('add3(3)(5); add3(7)(8); add3.result = ' + add3.result);

  add4(3);
  add4(5);
  add4(7);
  add4(8);
  console.log('add4(3); add4(5); add4(7); add4(8); add4.result = ' + add4.result);

}(factory));

当add函数最终返回一个数字时,该数字不能作为函数调用。 通常的方法是指定在最终返回数字之前需要多少个参数,如其他答案中所建议的那样。 一些答案建议使用函数终止符( 0undefined )作为一种新颖的方法,但是从评论中可以看出,OP是

“寻找解决方案而无需以()或(0)结束”

使用复合模式,我们可以返回一个函数,该函数也可以通过赋予结果属性来用作数字。 这样我们就可以通过越来越多的函数调用来累积结果。 如果我们需要单独add我们可以使用工厂函数来创建add实例。 或者,当需要单独添加时,我们可以将单个add添加实例重置为零。

我还编写了一组自定义测试断言,其中包含以下传递测试用例:

var testCases = [
        {arguments: [0], expectedResult: 0},
        {arguments: [5, 0, 0, 5, 10, -1], expectedResult: 19},
        {arguments: [1], expectedResult: 1},
        {arguments: [1, 2], expectedResult: 3},
        {arguments: [1, 2, 3], expectedResult: 6},
        {arguments: [3, 5, 7, 8], expectedResult: 23},
        {arguments: [3, 4], expectedResult: 7},//{acc: 1000}
        {arguments: [1, 2], expectedResult: 1003, factoryOptions: {acc: 1000}},
        //example tests with logging to debug
        //{arguments: [1, 2], expectedResult: 3, factoryOptions: {f: add, log: addLog}},
        //{arguments: [3, 4], expectedResult: -7, factoryOptions: {f: sub, log: subLog}},
        {arguments: [3, 4], expectedResult: -7, factoryOptions: {f: sub}}
      ]

我使用了控制反转,并将reduce函数作为工厂的可选参数。 例如,代替使用add您可以使用sub (即x-y),如测试用例中所示。

简而言之,问题的标题和正文是关于不同的事情。

首先,函数式编程不是关于n元函数。 您可以构造之前显示的示例,例如使用递归(以保持不变性); 但是,你会牺牲整个函数。 例如,如果您没有验证为非零的外部参数,则0终止的函数可能会在运行时崩溃。

然后,启用整体的解决方案要么接受非零整数(JavaScript只有'number'类型),要么调用零来获取值。

或者,monad可以允许您组合数字的属性,同时返回可以进一步绑定的函数。 (但那时你最终不会得到一个纯粹的'数字',而是一个自定义'添加'monad。很有用,但必须实现。)

否则,add函数可能只是列表的总和。 然后它可以有1到N个元素进行计算,它就不是它的参数。

现在,至于为什么变量不是很有用,原因很重要。 首先,如果你有一个函数f(a)(b)(c)(d)...(n)它的类型是什么? 如果没有类型,您将失去该领域的许多核心方面。 如果它总是返回它自己的类型(这是可能的,由递归示例显示)那么它是无用的,因为它实际上不能产生副作用。 现在为了使它有用,在保持纯度的同时,我们不能只用零限制域(即),因为那时我们'撒谎',因为零是有效输入的一部分; 结果不会与函数的类型相同。 因此,如果我们要创建一个包装函数,将所有输入验证为非0(并忽略0),并在最后一个成员调用0之后,我们执行的操作与之前讨论的完全相同,只是在不同的方式。

例如,我们可以将'Maybe number'作为输入,当它不是数字时,返回结果(因此总体而不是功能)。 或者我们可以有一个列表,在列表结束后,我们再一次'检测'一个变化(在另一个地方,即看看矢量的大小是否已经用尽;或者是否没有更多成员)一个列表。)

无论哪种方式实现,它都做同样的事情。 不同之处在于可用性,错误数量,可读性,可维护性,可接受的数据类型,语言或透视。

关于为什么这一切都如此复杂的说明:自己提供一个参数与调用函数完全相同。 因此f(a)(b)(c)(d)有4个应用,它们都不是“最后一个”,它们是有序的,但是没有隐含的有限边界。 其创建再次循环回到我之前关于如何与不同实现相同的问题的陈述。

所有解决方案都以这种或那种方式遵循这种模式。 链接二进制加法运算会产生与递归非常相似的数据流; 但是,边界是硬编码的。 递归类似于OOP样式,其中累加总和并且可以在任何点检索。 这类似于'Add'Monad,它既包含加法函数又包含当前状态,因此在添加函数之后基本上“下一个”用户将丢弃该函数并保持状态。

我认为就给定问题而言最简单的解决方案是:[1,2,3,4,5] .reduce(函数add(sum,x){sum + x},0)

暂无
暂无

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

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