Вернуться к уроку

Сумма произвольного количества скобок

важность: 2

Напишите функцию sum, которая будет работать так:

sum(1)(2) == 3; // 1 + 2
sum(1)(2)(3) == 6; // 1 + 2 + 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15

Количество скобок может быть любым.

Пример такой функции для двух аргументов – есть в решении задачи Сумма через замыкание.

Подсказка

Чтобы sum(1), а также sum(1)(2) можно было вызвать новыми скобками – результатом sum должна быть функция.

Но эта функция также должна уметь превращаться в число. Для этого нужно дать ей соответствующий valueOf. А если мы хотим, чтобы и в строковом контексте она вела себя так же – то toString.

Решение

Функция, которая возвращается sum, должна накапливать значение при каждом вызове.

Удобнее всего хранить его в замыкании, в переменной currentSum. Каждый вызов прибавляет к ней очередное значение:

function sum(a) {

  var currentSum = a;

  function f(b) {
    currentSum += b;
    return f;
  }

  f.toString = function() {
    return currentSum;
  };

  return f;
}

alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
alert( sum(6)(-1)(-2)(-3) ); // 0
alert( sum(0)(1)(2)(3)(4)(5) ); // 15

При внимательном взгляде на решение легко заметить, что функция sum срабатывает только один раз. Она возвращает функцию f.

Затем, при каждом запуске функция f добавляет параметр к сумме currentSum, хранящейся в замыкании, и возвращает сама себя.

В последней строчке f нет рекурсивного вызова.

Вот так была бы рекурсия:

function f(b) {
  currentSum += b;
  return f(); // <-- подвызов
}

А в нашем случае, мы просто возвращаем саму функцию, ничего не вызывая.

function f(b) {
  currentSum += b;
  return f; // <-- не вызывает сама себя, а возвращает ссылку на себя
}

Эта f используется при следующем вызове, опять возвратит себя, и так сколько нужно раз. Затем, при использовании в строчном или численном контексте – сработает toString, который вернет текущую сумму currentSum.