#javascript #arrays #recursion
#javascript #массивы #рекурсия
Вопрос:
Эта логика и вывод кажутся нелогичными, может кто-нибудь объяснить эту логику описательно?
function countdown(n){
if(n < 1){
return [];
}
else{
const countArray = countdown(n-1)
countArray.push(n);
return countArray;
}
}
console.log(countdown(5));
Комментарии:
1. Вы хотите объяснение кода?
2. Попробуйте добавить
console.log(countArray)
после первоначального назначенияconst countArray = countdown(n-1)
, и вы увидите, что происходит.3. Да, я пытаюсь понять оценку вывода. Рекурсия здесь выполняется по-другому?
4. @Nick Пустой массив присваивается countArray, но как увеличивается значение? Разве он не должен выходить из функции после нажатия ‘1’?
Ответ №1:
const countArray = countdown(n-1)
countArray.push(n);
Поскольку вы вызываете countdown(n-1) перед отправкой n в массив, countdown(n-1) вычисляется первым.
function countdown(n,countArray ){
if(n < 1){
return [];
}
else{
countArray.push(n);
countdown(n-1,countArray);
return countArray;
}
}
console.log(countdown(5,[]));
Результатом этого будет [5,4,3,2,1]
Комментарии:
1. Это не объясняет результат. Поскольку значение вычисляется один раз, не должен ли результат прикрепленного кода быть [1] ?
2. [1] возвращается к вызову функции countdown(2) при const countArray = countdown(n-1), затем countArray.push(n); возвращает countArray; вычисляет и возвращает [1,2] к вызову функции countdown(3) при const countArray = countdown(n-1)
3. При рекурсии после вызова функции она добавляется в начало стека вызовов, а последняя функция вычисляется первой и возвращает результат следующей последней. В этом случае стек вызовов будет обратным отсчетом (1) обратным отсчетом (2) обратным отсчетом (3) обратным отсчетом (4) обратным отсчетом (5)
Ответ №2:
Один из способов понять рекурсию — думать о ней как о математической индукции. В индукции мы показываем, что утверждение верно для всех натуральных чисел, показывая, что оно верно для n = 1
, и что если оно верно для всех значений, меньших заданного n
, то оно верно для n
. Этих двух фактов достаточно, чтобы показать, что это верно для всех n
s.
Чтобы понять рекурсивную функцию, нам нужно показать, что базовый вариант выполняет то, что мы ожидаем, и что если меньшие случаи верны, то это верно для текущего случая. Тогда мы знаем, что это верно для всех случаев. Иногда бывает сложно решить, какой случай меньше, но обычно это просто, как здесь.
Давайте попробуем это понимание рекурсии в вашей функции. Но сначала давайте отметим, что существует проблема с именованием. Функция, которую мы пытаемся описать count down
, этого не делает counts up
. Предположительно, вызванная функция countDown
при заданном 5
обратном отсчете оттуда вернется [5, 4, 3, 2, 1]
. Ваш подсчитывается, уступая [1, 2, 3, 4, 5]
. Я думаю, вы просили объяснить, как это работает, не спрашивая, как заставить обратный отсчет работать. Если вы ищете это, пожалуйста, задайте отдельный вопрос. Но я предполагаю, что вы имеете в виду countUp
, и буду использовать этот вариант в обсуждении ниже:
function countUp(n){
if(n < 1){
return [];
}
else{
const countArray = countUp(n-1)
countArray.push(n);
return countArray;
}
}
Сначала у нас есть базовый вариант:
if(n < 1){
return [];
}
если n
меньше 1, то чисел для обратного отсчета нет, поэтому правильным ответом является пустой массив.
Теперь мы делаем предположение: мы предполагаем, что рекурсия работает для всех значений, меньших нашего текущего n
. Если мы можем показать, что тогда это верно для нашего текущего n
, мы показали, что это верно для всех n
s `.
Теперь у нас есть
else{
const countArray = countUp(n-1)
countArray.push(n);
return countArray;
}
Эта строка является важной:
const countArray = countUp(n-1)
Мы сделали предположение, что наша рекурсия работает для меньших версий n
. Итак, мы можем предположить, что countUp(n-1)
это дает [1, 2, 3, ..., (n - 1)]
. Затем мы вызываем countArray.push(n)
, так что теперь равно [1, 2, 3, ..., (n - 1), n]
, что именно так, как мы и хотели. Мы возвращаем это значение, и все готово.
Мы только что продемонстрировали, что для каждого положительного n
countUp(n)
результата [1, 2, 3, ..., n]
.
Если это поможет вам быть более конкретным, мы можем рассмотреть случай, n = 5
. Тест n < 1
завершается неудачей, поэтому мы переходим к else
блоку, где мы вызываем countUp(4)
. Но мы уже предположили, что один сработал, поэтому мы знаем, что он вернется [1, 2, 3, 4]
. Мы нажимаем 5
на этот массив и возвращаем результат, возвращая обратно [1, 2, 3, 4, 5]
, хотя, конечно, мы могли бы продвинуть это дальше и отметить, что при генерации [1, 2, 3, 4]
мы сначала вызываем countUp(3)
, получая [1, 2, 3]
, а затем нажимаем 4
, и чтобы получить это, мы вызываем countUp(2)
, получая [1, 2]
и т.д. Но это покажет только то, для чего это работает 5
. Иногда это полезное упражнение, но чтобы по-настоящему понять это, вы можете выйти за рамки конкретного случая и работать с предположением, которое я предлагаю, и посмотреть, как это показывает работу рекурсии.
Мы также можем использовать эту технику при написании рекурсивной функции. Здесь мы пишем другую версию той же функции, явно используя нашу технику.
Нам нужна рекурсивная функция, которая, учитывая положительное целое n
число, возвращает массив [1, 2, 3, ..., n]
. Итак, для нашего базового варианта мы можем выбрать несколько вариантов. Мы могли бы проверить, если n == 1
и вернуть [1]
. Мы могли бы, если бы захотели, проверить n == 2
и вернуть [1, 2]
, но тогда это не сработало бы n == 1
. Но более простым было бы то, что произойдет, если n < 1
. В этом случае мы хотим вернуть пустой массив. Итак, мы можем начать с условного выражения:
const countUp = (n) =>
n < 1
? []
: /* now what goes here? */
Ну, мы можем сделать предположение, что наша функция уже работает для чего-то меньшего, чем n
, поэтому мы можем предположить, что она работает для n - 1
, и countUp(n - 1)
даст [1, 2, 3, ..., (n - 1)]
результат . Ну, чтобы превратить это в [1, 2, 3, ..., (n - 1), n]
, требуется только объединение n
в конце предыдущего вызова. Мы можем написать это так:
const countUp = (n) =>
n < 1
? []
: countUp(n - 1).concat(n)
Или, используя более современный JS, как
const countUp = (n) =>
n < 1
? []
: [...countUp(n - 1), n]
Ответ №3:
Поскольку он возвращает пустой массив []
как countArray
функцию when n < 1
, n можно поместить в countArray . Перед тем, как 1 нажимается countArray
, функция массива выполняется как countdown(n-1)
. 2 нажимается на массив [1]
, и функция возвращается к countdown(5)
.
function countdown(n){
if(n < 1){
return [];
}
else{
const countArray = countdown(n-1)
countArray.push(n);
return countArray;
}
}
console.log(countdown(5));