"добавить", которая работает с различными комбинациями цепочек/аргументов

Я пытаюсь написать функцию добавления, которая будет работать во многих сценариях.

add(2,2,2) //6
add(2,2,2,2) //8
add(2)(2)(2) // 6
add(2)(2)(2,2).value() //8
add(2,2)(2) + 2 //8
add(2).add(2) //4
add(2,2,2).add(2).add(2,2).value() //12
add(2,2,2).add(2).value() //8

Это то, что у меня есть до сих пор:

function add(){
    var sum = 0;
    for( var i in arguments ){
        sum += arguments[i];
    }

    var ret = add.bind(null, sum);

    ret.value = function () {
      return sum;
    }

    ret.add = function () {
      for( var i in arguments ){
        sum += arguments[i];
      }
      return sum;
    }

    ret.valueOf = function(){ return sum; };
    
    return ret;
}

console.log(add(2,2,2));
console.log(add(2,2,2,2));
console.log(add(2)(2)(2));
console.log(add(2)(2)(2,2).value());
console.log(add(2,2)(2) + 2);
console.log(add(2).add(2));
console.log(add(2,2,2).add(2).value());
console.log(add(2,2,2).add(2).add(2,2).value());

Ответы

Ответ 1

Посмотрев на то, как вы используете arguments аналогичным образом в двух разных местах, ясно, что вы дублируете функциональность, и именно поэтому вы сталкиваетесь с этой проблемой с необходимостью "бесконечно гнездовать" .value() метод.

Ключом к пониманию является то, что add() может возвращать функцию, которая ссылается сама на себя как свое собственное свойство add. Это позволит add(1,2)(3) вести себя точно так же, как add(1,2).add(3). Это можно сделать так:

function add() {
  var sum = Array.prototype.reduce.call(arguments, function(l, r) {
    return l + r;
  }, 0);

  var ret = add.bind(null, sum);
  ret.add = ret;

  ret.value = ret.valueOf = Number.prototype.valueOf.bind(sum);      
  ret.toString = Number.prototype.toString.bind(sum);

  return ret;
}

snippet.log(add(2,2,2));
snippet.log(add(2,2,2,2));
snippet.log(add(2)(2)(2));
snippet.log(add(2)(2)(2,2).value());
snippet.log(add(2,2)(2) + 2);
snippet.log(add(2).add(2));
snippet.log(add(2,2,2).add(2).value());
snippet.log(add(2,2,2).add(2).add(2,2).value());

snippet.log(add(1, 2, 3)(4, 5).add(6, 7)(8).add(9, 10));
snippet.log(add(5,4)(3).add(2)(1) * 10);
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Ответ 2

Так как вы возвращаете сумму в функции ret.add, почему возникает ошибка, попробуйте что-то вроде этого, надейтесь, что она решит вашу проблему.

function add(){
    var sum = 0;
    for( var i in arguments ){
        sum += arguments[i];
    }

    var ret = add.bind(null, sum);

    ret.value = function () {
      return sum;
    }

    ret.add = function () {
      for( var i in arguments ){
        sum += arguments[i];
      }
      return ret;
    }

    ret.valueOf = function(){ return sum; };

    return ret;
}

Ответ 3

Также им нужно всегда возвращать ints (не строки), и кажется, что иногда они делают, а в других случаях они этого не делают?

Да, это определенно концептуальная проблема. Эти две вещи, которые вы хотите, несовместимы. Является ли add(2,2,2) числом или чем-то с помощью метода add?

add(2,2,2) //6
add(2,2,2).add(2).value() //8

Даже если есть способ добавить методы в nubmers, я бы очень рекомендовал держать вещи простыми и всегда требовать ".value()", чтобы завершить цепочку. Таким образом, все вызовы ".add" возвращают "объект-сумматор", и все вызовы на ".value" возвращают правильный номер.

похоже, что мне нужно было бы вложить функции добавления, если бы я хотел объединить более двух вместе, а также добавить функцию значения для каждого из них, но, очевидно, я пропустил что-то простое, что позволит мне связать их насколько мне нравится, и вызывать ценность для любого из них.

Ответ на этот вопрос - использование рекурсивных функций. Вот функция, которая создает объект "сумматор", о котором я упоминал ранее:

function sumArray(arr){
    var s = 0;
    for(var i=0; i<arr.length; i++){
        s += arr[i];
    }
    return s;
}

function mkAdder(currSum){
    return {
        value: function(){
            return currSum;
        },
        valueOf: function(){
            return currSum;
        },
        add: function(/**/){
            return mkAdder(currSum + sumArray(arguments));
        }
    }
}

Тогда ваша начальная функция добавления будет выглядеть так:

function add(/**/){
    return mkAdder(sumArray(arguments));
}

Ответ 4

Я попытался импровизировать с помощью this. Работает во всех случаях.

function add(){
    var sum =  this instanceof Number?this: 0;
    for( var i in arguments ){
        sum += arguments[i];
    }

    var ret = add.bind(sum);
    ret.add = ret;
    ret.value = ret.valueOf = function() { return sum; };

    ret.toString = sum.toString.bind(sum);

    return ret;
}

JS-Fiddle