Подтвердить что ты не робот

Вычитание рекурсии Javascript

Пример 1

function x(num) {
  if (num == 0) {
   return 1;
  }
  else {
   return (num * x(num - 1));
 }
}

x(8);

8 * 7 * 6 * 5 * 4 * 3 * 2 * 1

Результат равен 40320

Пример 2

function x(num) {
  if (num == 0) {
   return 0;
  }
  else {
   return (num + x(num - 1));
 }
}

x(8);

8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0

Результат равен 36, как ожидалось

Пример 3

function x(num) {
  if (num == 0) {
   return 0;
  }
  else {
   return (num - x(num - 1));
 }
}

x(8);

8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0

Результат 4

Может кто-нибудь объяснить, почему?

Не должно быть ответа -20?

4b9b3361

Ответ 1

Расчет с вашей функцией в основном идет справа налево:

  8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
= 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1
= 8 - 7 - 6 - 5 - 4 - 3 - 1
= 8 - 7 - 6 - 5 - 4 - 2
= 8 - 7 - 6 - 5 - 2
= 8 - 7 - 6 - 3
= 8 - 7 - 3
= 8 - 4
= 4

Это означает, что каждый раз, когда функция x рекурсивно вызывается, выполняется вычисление самого правильного выражения.

Эффективно, "стек" выглядит так:

  8 - x(8 - 1)
= 8 - (7 - x(7 - 1))
= 8 - (7 - (6 - x(6 - 1)))
= 8 - (7 - (6 - (5 - x(5 - 1))))
= 8 - (7 - (6 - (5 - (4 - x(4 - 1)))))
= 8 - (7 - (6 - (5 - (4 - (3 - x(3 - 1))))))
= 8 - (7 - (6 - (5 - (4 - (3 - (2 - x(2 - 1)))))))
= 8 - (7 - (6 - (5 - (4 - (3 - (2 - (1 - x(1 - 1))))))))
= 8 - (7 - (6 - (5 - (4 - (3 - (2 - (1 - 0)))))))
= 8 - (7 - (6 - (5 - (4 - (3 - (2 - 1))))))
= 8 - (7 - (6 - (5 - (4 - (3 - 1)))))
= 8 - (7 - (6 - (5 - (4 - 2))))
= 8 - (7 - (6 - (5 - 2)))
= 8 - (7 - (6 - 3))
= 8 - (7 - 3)
= 8 - 4
= 4

Оценка JavaScripts для "чистого" вычитания:

8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0; // -20

Ответ 2

Потому что ваша функция эффективно вычисляет это право налево:

8 - (7 - (6 - (5 - (4 - (3 - (2 - (1 - 0))))))) => 4

И не:

8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 => -20

Ответ 3

Первое вычитание вашей рекурсии начинается с двух последних чисел и выполняется назад:

x = 8-7-6-5-4-3-2-(1-0)
x = 8-7-6-5-4-3-(2-1)
x = 8-7-6-5-4-(3-1)
x = 8-7-6-5-(4-2)
x = 8-7-6-(5-2)
x = 8-7-(6-3)
x = 8-(7-3)
x = (8-4)
x = 4

Ответ 4

Уравнение 8-7-6-5-4-3-2-1-0, написанное с рекурсией, должно что-то повторить. 8 не является частью этого, потому что он не вычитает его. Поэтому вы должны сделать что-то вроде:

= 8 + ( "recursion starting from 7")
= 8 + ( -7 + "recursion starting from 6")
...
= 8 + ( -7 + -6 + -5 + -4 + -3 + -2 + "recursion starting from 1")
= 8 + ( -7 + -6 + -5 + -4 + -3 + -2 + -1 + "recursion starting from 0")
= 8 + ( -7 + -6 + -5 + -4 + -3 + -2 + -1 + -0 /* end case */ )

Код будет что-то вроде

   function x(num, first){
      if (first){
        return num + x(num-1, false);
      } else if (num > 0){
        return -num + x (num-1, false);
      } else {
        return 0;
      }

    }

Ответ 5

Как отвечали другие, вычитание выполняется в обратном порядке, справа налево:

x = 8-7-6-5-4-3-2-(1-0)
x = 8-7-6-5-4-3-(2-1)
x = 8-7-6-5-4-(3-1)
x = 8-7-6-5-(4-2)
x = 8-7-6-(5-2)
x = 8-7-(6-3)
x = 8-(7-3)
x = (8-4)
x = 4

То, что вы ищете, является обратным добавлению, которое можно записать следующим образом:

function addition(num) {
    if (num == 0) {
        return 0;
    }
    else {
        return (num + addition(num - 1));
    }
}

function subtraction(num) {
    return num - addition(num - 1);
}

Вызывающее вычитание (8) даст -20, потому что оно вычитает сумму всех меньших чисел, тогда как ваша исходная функция вычитает все меньшие числа друг от друга.

Ответ 6

Продумать мой комментарий...

Вычитание (в отличие от умножения и добавления, объясняющее, почему работают ваши другие примеры) не является коммутативным, а оценка JavaScript остается справа, тогда как ваша функция эффективно делает это справа налево.

Чтобы получить правильный результат с вашим существующим кодом, вы можете использовать тот факт, что вычитание является добавлением отрицательного числа. В цепочке только самое левое число положительно. Повторное использование вашей функции sum:

function sub(num) {
    return num - sum(num - 1)
}

Для чисто рекурсивного однофункционального решения потребуется дополнительный аргумент, чтобы знать, где остановиться (начиная с нуля), или сделать особый случай для первого вызова (добавление вместо вычитания, за исключением первого вызова).

Конечно, если вы не против обмана с арифметикой, вы можете линеаризовать на

function sub(num){ return num - (num*(num-1)/2) }

Ответ 7

Почему не -20 и вычитание не начинается с 8? потому что функция выполняет цикл через все эти числа, после достижения 0 вычитания начала функции. Но перед выполнением всех функций x(num - 1). Вы можете просто понять это из 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1, что функция умножает сначала 2 * 1 = 2, а затем 2 * 3 = 6, затем 6 * 4...