update
This commit is contained in:
parent
962caebbb7
commit
87bf53d076
1825 changed files with 94929 additions and 0 deletions
45
1-js/2-first-steps/19-recursion/1-sum-to/solution.md
Normal file
45
1-js/2-first-steps/19-recursion/1-sum-to/solution.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
Решение **с использованием цикла**:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
var sum = 0;
|
||||
for(var i=1; i<=n; i++) {
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
Решение через **рекурсию**:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
if (n == 1) return 1;
|
||||
return n + sumTo(n-1);
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
Решение **по формуле**: `sumTo(n) = n*(n+1)/2`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
return n*(n+1)/2;
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
P.S. Надо ли говорить, что решение по формуле работает быстрее всех? Это очевидно. Оно использует всего три операции для любого `n`, а цикл и рекурсия требуют как минимум `n` операций сложения.
|
||||
|
||||
Вариант с циклом -- второй по скорости. Он быстрее рекурсии, так как операций сложения столько же, но нет дополнительных вычислительных затрат на организацию вложенных вызовов.
|
||||
|
||||
Рекурсия в данном случае работает медленнее всех.
|
||||
|
||||
P.P.S. Существует ограничение глубины вложенных вызовов, поэтому рекурсивный вызов `sumTo(100000)` выдаст ошибку.
|
33
1-js/2-first-steps/19-recursion/1-sum-to/task.md
Normal file
33
1-js/2-first-steps/19-recursion/1-sum-to/task.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Вычислить сумму чисел до данного
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите функцию `sumTo(n)`, которая для данного `n` вычисляет сумму чисел от 1 до `n`, например:
|
||||
|
||||
```js
|
||||
sumTo(1) = 1
|
||||
sumTo(2) = 2 + 1 = 3
|
||||
sumTo(3) = 3 + 2 + 1 = 6
|
||||
sumTo(4) = 4 + 3 + 2 + 1 = 10
|
||||
...
|
||||
sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
|
||||
```
|
||||
|
||||
Сделайте три варианта решения:
|
||||
<ol>
|
||||
<li>С использованием цикла.</li>
|
||||
<li>Через рекурсию, т.к. `sumTo(n) = n + sumTo(n-1)` для `n > 1`.</li>
|
||||
<li>С использованием формулы для суммы [арифметической прогрессии](http://ru.wikipedia.org/wiki/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F).</li>
|
||||
</ol>
|
||||
|
||||
Пример работы вашей функции:
|
||||
|
||||
```js
|
||||
function sumTo(n) { /*... ваш код ... */ }
|
||||
|
||||
alert( sumTo(100) ); // 5050
|
||||
```
|
||||
|
||||
**Какой вариант решения самый быстрый? Самый медленный? Почему?**
|
||||
|
||||
**Можно ли при помощи рекурсии посчитать `sumTo(100000)`? Если нет, то почему?**
|
25
1-js/2-first-steps/19-recursion/2-factorial/solution.md
Normal file
25
1-js/2-first-steps/19-recursion/2-factorial/solution.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
По свойствам факториала, как описано в условии, `n!` можно записать как `n * (n-1)!`.
|
||||
|
||||
То есть, результат функции для `n` можно получить как `n`, умноженное на результат функции для `n-1`, и так далее до `1!`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function factorial(n) {
|
||||
return (n!=1) ? n*factorial(n-1) : 1;
|
||||
}
|
||||
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
Базисом рекурсии является значение `1`. А можно было бы сделать базисом и `0`. Тогда код станет чуть короче:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function factorial(n) {
|
||||
return n ? n*factorial(n-1) : 1;
|
||||
}
|
||||
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
В этом случае вызов `factorial(1)` сведется к `1*factorial(0)`, будет дополнительный шаг рекурсии.
|
29
1-js/2-first-steps/19-recursion/2-factorial/task.md
Normal file
29
1-js/2-first-steps/19-recursion/2-factorial/task.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Вычислить факториал
|
||||
|
||||
[importance 4]
|
||||
|
||||
*Факториа́л числа* -- это число, умноженное на "себя минус один", затем на "себя минус два" и так далее, до единицы. Обозначается `n!`
|
||||
|
||||
Определение факториала можно записать как:
|
||||
|
||||
```js
|
||||
n! = n*(n-1)*(n-2)*...*1
|
||||
```
|
||||
|
||||
Примеры значений для разных `n`:
|
||||
|
||||
```js
|
||||
1! = 1
|
||||
2! = 2*1 = 2
|
||||
3! = 3*2*1 = 6
|
||||
4! = 4*3*2*1 = 24
|
||||
5! = 5*4*3*2*1 = 120
|
||||
```
|
||||
|
||||
Задача -- написать функцию `factorial(n)`, которая возвращает факториал числа `n!`, используя рекурсивный вызов.
|
||||
|
||||
```js
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
Подсказка: обратите внимание, что `n!` можно записать как `n * (n-1)!`, например `3! = 3*2! = 3*2*1! = 6`
|
|
@ -0,0 +1,94 @@
|
|||
# Вычисление рекурсией (медленное)
|
||||
|
||||
Решение по формуле, используя рекурсию:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function fib(n) {
|
||||
return n <= 1 ? n : fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
// fib(77); // не запускаем, подвесит браузер
|
||||
```
|
||||
|
||||
При больших значениях `n` оно будет работать очень медленно. Например, `fib(77)` уже будет вычисляться очень долго.
|
||||
|
||||
Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляются много раз. Например, посмотрим на отрывок вычислений:
|
||||
|
||||
```js
|
||||
...
|
||||
fib(5) = fib(4) + fib(3)
|
||||
fib(4) = fib(3) + fib(2)
|
||||
...
|
||||
```
|
||||
|
||||
Здесь видно, что значение `fib(3)` нужно одновременно и для `fib(5)` и для `fib(4)`. В коде оно будет вычислено два раза, совершенно независимо.
|
||||
|
||||
Можно это оптимизировать, запоминая уже вычисленные значения, получится гораздо быстрее. Альтернативный вариант -- вообще отказаться от рекурсии, а вместо этого в цикле начать с первых значений `1`, `2`, затем из них получить `fib(3)`, далее `fib(4)`, затем `fib(5)` и так далее, до нужного значения.
|
||||
|
||||
Это решение будет наиболее эффективным. Попробуйте его написать.
|
||||
|
||||
# Алгоритм вычисления в цикле
|
||||
|
||||
Будем идти по формуле слева-направо:
|
||||
|
||||
```js
|
||||
var a = 1, b = 1; // начальные значения
|
||||
var c = a + b; // 2
|
||||
|
||||
/* переменные на начальном шаге:
|
||||
a b c
|
||||
1, 1, 2
|
||||
*/
|
||||
```
|
||||
|
||||
Теперь следующий шаг, присвоим `a` и `b` текущие 2 числа и получим новое следующее в `c`:
|
||||
|
||||
```js
|
||||
a = b, b = c;
|
||||
c = a + b;
|
||||
|
||||
/* стало так (еще число):
|
||||
a b c
|
||||
1, 1, 2, 3
|
||||
*/
|
||||
```
|
||||
|
||||
Следующий шаг даст нам еще одно число последовательности:
|
||||
|
||||
```js
|
||||
a = b, b = c;
|
||||
c = a + b;
|
||||
|
||||
/* стало так (еще число):
|
||||
a b c
|
||||
1, 1, 2, 3, 5
|
||||
*/
|
||||
```
|
||||
|
||||
Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды.
|
||||
|
||||
P.S. Этот подход к вычислению называется [динамическое программирование снизу-вверх](http://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
|
||||
|
||||
# Код для вычисления в цикле
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function fib(n) {
|
||||
var a = 1, b = 1;
|
||||
for (var i = 3; i <= n; i++) {
|
||||
var c = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
alert( fib(77)); // 5527939700884757
|
||||
```
|
||||
|
||||
Цикл здесь начинается с `i=3`, так как первое и второе числа Фибоначчи заранее записаны в переменные `a=1`, `b=1`.
|
23
1-js/2-first-steps/19-recursion/3-fibonacci-numbers/task.md
Normal file
23
1-js/2-first-steps/19-recursion/3-fibonacci-numbers/task.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Числа Фибоначчи
|
||||
|
||||
[importance 5]
|
||||
|
||||
Последовательность [чисел Фибоначчи](http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8) имеет формулу <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. То есть, следующее число получается как сумма двух предыдущих.
|
||||
|
||||
Первые два числа равны `1`, затем `2(1+1)`, затем `3(1+2)`, `5(2+3)` и так далее: `1, 1, 2, 3, 5, 8, 13, 21...`.
|
||||
|
||||
Числа Фибоначчи тесно связаны с [золотым сечением](http://ru.wikipedia.org/wiki/%D0%97%D0%BE%D0%BB%D0%BE%D1%82%D0%BE%D0%B5_%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5) и множеством природных явлений вокруг нас.
|
||||
|
||||
Напишите функцию `fib(n)`, которая возвращает `n-е` число Фибоначчи. Пример работы:
|
||||
|
||||
```js
|
||||
function fib(n) { /* ваш код */ }
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
alert( fib(77)); // 5527939700884757
|
||||
```
|
||||
|
||||
**Все запуски функций из примера выше должны срабатывать быстро.**
|
||||
|
||||
|
273
1-js/2-first-steps/19-recursion/article.md
Normal file
273
1-js/2-first-steps/19-recursion/article.md
Normal file
|
@ -0,0 +1,273 @@
|
|||
# Рекурсия, стек
|
||||
|
||||
В коде функции могут вызывать другие функции для выполнения подзадач. Частный случай подвызова -- когда функция вызывает сама себя. Это называется *рекурсией*.
|
||||
|
||||
В этой главе мы рассмотрим, как рекурсия устроена изнутри, и как её можно использовать.
|
||||
[cut]
|
||||
|
||||
|
||||
## Реализация pow(x, n) через рекурсию
|
||||
|
||||
Чтобы возвести `x` в натуральную степень `n` -- можно умножить его на себя `n` раз в цикле:
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
var result = x;
|
||||
for(var i=1; i<n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
А можно поступить проще.
|
||||
|
||||
Ведь <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>, т.е. можно вынести один `x` из-под степени. Иначе говоря, значение функции `pow(x,n)` получается из `pow(x, n-1)` умножением на `x`.
|
||||
|
||||
Этот процесс можно продолжить. Например, вычислим `pow(2, 4)`:
|
||||
|
||||
```js
|
||||
pow(2, 4) = 2 * pow(2, 3) = 2 * 2 * pow(2, 2) = 2 * 2 * 2 * pow(2, 1) = 2 * 2 * 2 * 2;
|
||||
```
|
||||
|
||||
Процесс перехода от `n` к `n-1` останавливается на `n==1`, так как очевидно, что `pow(x,1) == x`.
|
||||
|
||||
Код для такого вычисления:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function pow(x, n) {
|
||||
if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
|
||||
return x * pow(x, n-1);
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
alert( pow(2, 3) ); // 8
|
||||
```
|
||||
|
||||
Говорят, что "функция `pow` *рекурсивно вызывает сама себя*" при `n != 1`.
|
||||
|
||||
Значение, на котором рекурсия заканчивается называют *базисом рекурсии*. В примере выше базисом является `1`.
|
||||
|
||||
Общее количество вложенных вызовов называют *глубиной рекурсии*. В случае со степенью, всего будет `n` вызовов. Максимальная глубина рекурсии ограничена и составляет около `10000`, но это число зависит от конкретного интерпретатора JavaScript и может быть в 10 раз меньше.
|
||||
|
||||
**Рекурсию используют, когда вычисление функции можно свести к её более простому вызову, а его -- еще к более простому, и так далее, пока значение не станет очевидно.**
|
||||
|
||||
## Контекст выполнения, стек
|
||||
|
||||
Теперь мы посмотрим, как работают рекурсивные вызовы. Для этого мы рассмотрим, как вообще работают функции, что происходит при вызове.
|
||||
|
||||
**У каждого вызова функции есть свой "контекст выполнения" (execution context).**
|
||||
|
||||
Контекст выполнения -- это служебная информация, которая соответствует текущему запуску функции. Она включает в себя локальные переменные функции.
|
||||
|
||||
Например, для вызова:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function pow(x, n) {
|
||||
if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
|
||||
return x * pow(x, n-1);
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
*!*
|
||||
alert( pow(2, 3) ); // (*)
|
||||
*/!*
|
||||
```
|
||||
|
||||
При запуске функции `pow` в строке `(*)` будет создан контекст выполнения, который будет хранить переменные `x = 2, n = 3`. Мы схематично обозначим его так:
|
||||
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
</ul>
|
||||
|
||||
Далее функция `pow` начинает выполняться. Вычисляется выражение `n != 1` -- оно равно `true`, ведь в текущем контексте `n=3`. Поэтому задействуется первая ветвь `if` :
|
||||
|
||||
```js
|
||||
if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
|
||||
*!*
|
||||
return x * pow(x, n-1);
|
||||
*/!*
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
```
|
||||
|
||||
Чтобы вычислить выражение `x * pow(x, n-1)`, требуется произвести запуск `pow` с новыми аргументами.
|
||||
|
||||
**При любом вложенном вызове JavaScript запоминает место, где он остановился в текущей функции в специальной внутренней структуре данных -- "стеке контекстов".**
|
||||
|
||||
Это как если бы мы куда-то ехали, но очень захотелось поесть. Можно остановиться у кафе, оставить машину, отойти, а потом, через некоторое время, вернуться к ней и продолжить дорогу.
|
||||
|
||||
Так и здесь -- мы запомним, где остановились в этой функции, пойдём выполним вложенный вызов, затем вернёмся и продолжим дорогу.
|
||||
|
||||
**После того, как текущий контекст выполнения сохранён в стеке контекстов, JavaScript приступает к выполнению вложенного вызова.**
|
||||
|
||||
В данном случае вызывается та же `pow`, однако, это абсолютно неважно. Для любых функций процесс одинаков.
|
||||
|
||||
**Создаётся новый контекст выполнения, и управление переходит в подвызов, а когда он завершён -- старый контекст достаётся из стека и выполнение внешней функции возобновляется.**
|
||||
|
||||
## Разбор примера
|
||||
|
||||
Разберём происходящее более подробно, начиная с вызова `(*)`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function pow(x, n) {
|
||||
if (n != 1) { // пока n!=1 сводить вычисление pow(..n) к pow(..n-1)
|
||||
return x * pow(x, n-1);
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
*!*
|
||||
alert( pow(2, 3) ); // (*)
|
||||
*/!*
|
||||
```
|
||||
|
||||
<dl>
|
||||
<dt>`pow(2, 3)`</dt>
|
||||
<dd>Запускается функция `pow`, с аргументами `x=2`, `n=3`. Эти переменные хранятся в контексте выполнения, схематично изображённом ниже:
|
||||
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
</ul>
|
||||
Выполнение в этом контексте продолжается, пока не встретит вложенный вызов в строке 3.
|
||||
</dd>
|
||||
<dt>`pow(2, 2)`</dt>
|
||||
<dd>В строке `3` происходит вложенный вызов `pow` с аргументами `x=2`, `n=2`. Для этой функции создаётся новый текущий контекст (выделен красным), а предыдущий сохраняется в "стеке":
|
||||
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
<li>Контекст: { x: 2, n: 2 }</li>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>`pow(2, 1)`</dt>
|
||||
<dd>Опять вложенный вызов в строке `3`, на этот раз -- с аргументами `x=2`, `n=1`. Создаётся новый текущий контекст, предыдущий добавляется в стек:
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
<li>Контекст: { x: 2, n: 2 }</li>
|
||||
<li>Контекст: { x: 2, n: 1 }</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>Выход из `pow(2, 1)`.</dt>
|
||||
<dd>При выполнении `pow(2, 1)`, в отличие от предыдущих запусков, выражение `n != 1` будет равно `false`, поэтому сработает вторая ветка `if..else`:
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
if (n != 1) {
|
||||
return x * pow(x, n-1);
|
||||
} else {
|
||||
*!*
|
||||
return x; // первая степень числа равна самому числу
|
||||
*/!*
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Здесь вложенных вызовов нет, так что функция заканчивает свою работу, возвращая `2`. Текущий контекст больше не нужен и удаляется из памяти, из стека восстанавливается предыдущий:
|
||||
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
<li>Контекст: { x: 2, n: 2 }</li>
|
||||
</ul>
|
||||
Возобновляется обработка внешнего вызова `pow(2, 2)`.
|
||||
</dd>
|
||||
<dt>Выход из `pow(2, 2)`.</dt>
|
||||
<dd>...И теперь уже `pow(2, 2)` может закончить свою работу, вернув `4`. Восстанавливается контекст предыдущего вызова:
|
||||
<ul class="function-execution-context">
|
||||
<li>Контекст: { x: 2, n: 3 }</li>
|
||||
</ul>
|
||||
Возобновляется обработка внешнего вызова `pow(2, 3)`.
|
||||
</dd>
|
||||
|
||||
<dt>Выход из `pow(2, 3)`.</dt>
|
||||
<dd>Самый внешний вызов заканчивает свою работу, его результат: `pow(2, 3) = 8`.</dd>
|
||||
</dl>
|
||||
|
||||
Глубина рекурсии в данном случае составила: **3**.
|
||||
|
||||
Как видно из иллюстраций выше, глубина рекурсии равна максимальному числу контекстов, одновременно хранимых в стеке.
|
||||
|
||||
[smart]
|
||||
В самом конце, как и в самом начале, выполнение попадает во внешний код, который находится вне любых функций.
|
||||
|
||||
Контекст, который соответствует самому внешнему коду, называют *"глобальный контекст"*. Естественно, он является начальной и конечной точкой любых вложенных подвызовов.
|
||||
[/smart]
|
||||
|
||||
Обратим внимание на требования к памяти. Рекурсия приводит к хранению всех данных для неоконченных внешних вызовов в стеке, в данном случае это приводит к тому, что возведение в степень `n` хранит в памяти `n` различных контекстов.
|
||||
|
||||
Реализация степени через цикл гораздо более экономна:
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
var result = x;
|
||||
for(var i=1; i<n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
У такой функции `pow` будет один контекст, в котором будут последовательно меняться значения `i` и `result`.
|
||||
|
||||
**Любая рекурсия может быть переделана в цикл. Как правило, вариант с циклом будет эффективнее.**
|
||||
|
||||
...Но зачем тогда нужна рекурсия? Да просто затем, что рекурсивный код может быть гораздо проще и понятнее!
|
||||
|
||||
Переделка в цикл может быть нетривиальной, особенно когда в функции, в зависимости от условий, используются разные рекурсивные подвызовы.
|
||||
|
||||
В программировании мы в первую очередь стремимся сделать сложное простым, а повышенная производительность нужна... Лишь там, где она действительно нужна. Поэтому красивое рекурсивное решение во многих случаях лучше.
|
||||
|
||||
Недостатки и преимущества рекурсии:
|
||||
|
||||
[compare]
|
||||
-Требования к памяти.
|
||||
-Ограничена максимальная глубина стека.
|
||||
+Краткость и простота кода.
|
||||
[/compare]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
Рекурсия -- это когда функция вызывает сама себя, с другими аргументами.
|
||||
|
||||
Существуют много областей применения рекурсивных вызовов. Здесь мы посмотрели на один из них -- решение задачи путём сведения её к более простой (с меньшими аргументами), но также рекурсия используется для работы с "естественно рекурсивными" структурами данных, такими как HTML-документы, для "глубокого" копирования сложных объектов.
|
||||
|
||||
Есть и другие применения, с которыми мы встретимся по мере изучения JavaScript.
|
||||
|
||||
Здесь мы постарались рассмотреть происходящее достаточно подробно, однако, если пожелаете, допустимо временно забежать вперёд и открыть главу [](/debugging-chrome), с тем чтобы при помощи отладчика построчно пробежаться по коду и посмотреть стек.
|
||||
|
||||
|
||||
|
||||
|
||||
[head]
|
||||
<style>
|
||||
.function-execution-context {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.function-execution-context li {
|
||||
float: left;
|
||||
clear: both;
|
||||
border: 1px solid black;
|
||||
font-family: "PT Mono", monospace;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
|
||||
.function-execution-context li:last-child {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
[/head]
|
Loading…
Add table
Add a link
Reference in a new issue