Назначение переменной позволяет избежать бесконечного цикла

#javascript #arrays #recursion

Вопрос:

У меня есть два фрагмента рекурсивного кода, намеревающегося рекурсивно распечатать половину массива, пока мы не доберемся до массивов длиной 1. Код без присвоения переменной выполняется бесконечно, в то время как код с присвоением переменной ведет себя так, как ожидалось.

Есть какие-нибудь зацепки, почему это так?

Бежит бесконечно, ОСТОРОЖНО

 function half(arr) {
  halfway = Math.floor((arr.length) / 2)
  console.log(arr)
  if (arr.length > 1) {
    half(arr.slice(0, halfway));
    half(arr.slice(halfway));
  }
  return
}

half([1, 2, 3, 4, 5]); 

Не работает бесконечно

 function half(arr) {
  halfway = Math.floor((arr.length) / 2)
  console.log(arr)
  if (arr.length > 1) {
    var a = arr.slice(0, halfway);
    var b = arr.slice(halfway);
    half(a);
    half(b);
  }
  return
}

half([1, 2, 3, 4, 5]); 

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

Комментарии:

1. var halfway = ... — В настоящее halfway время это глобальная переменная, которая блокируется рекурсивными вызовами.

2. Может ли кто-нибудь ответить, почему первый код не выводит [1,2,3,4,5] ?

3. @Тушаршахи, потому что видимые эффекты вызова функции ставятся в очередь, но никогда не получают шанса появиться, потому что бесконечный цикл не заканчивается.

4. @NiettheDarkAbsol, но другие вещи печатаются правильно? И [1,2,3,4,5] должно быть абсолютно первым, что нужно напечатать. Но не делает этого?

Ответ №1:

Потому что ему не хватает var let и const halfway он имеет глобальный охват, как если бы вы написали window.halfway . В результате все рекурсивные вызовы изменяют и используют одну и ту же единственную переменную.

В 1-й функции значение изменяется в первом рекурсивном вызове, прежде чем его можно будет использовать во втором рекурсивном вызове. В моем тестировании это фактически привело к своего рода ошибке переполнения стека (или, скорее, ошибке максимального размера стека вызовов), очень подходящей для этого сайта :-).

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

Проблема решена с помощью const :

 function half1(arr) {
  const halfway = Math.floor((arr.length) / 2)
  console.log(arr.toString())
  if (arr.length > 1) {
    half1(arr.slice(0, halfway));
    half1(arr.slice(halfway));
  }
  return
}

function half2(arr) {
  const halfway = Math.floor((arr.length) / 2)
  console.log(arr.toString())
  if (arr.length > 1) {
    var a = arr.slice(0, halfway);
    var b = arr.slice(halfway);
    half2(a);
    half2(b);
  }
  return
}

const data = [1, 2, 3, 4, 5];
half1(data);
console.log("------------------------")
half2(data); 

Последнее примечание: вся проблема была бы обнаружена и предотвращена компилятором JS, если бы вы поместили 'use strict'; ее поверх своего кода. Мне не очень нравится, как неуклюже работает эта директива (почему размещение «мертвой и неиспользуемой» строки поверх вашего кода имеет такой особый и далеко идущий эффект?), Но нам придется использовать то, что мы можем получить.