0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>В видео есть неточность: в формуле вычисления<a>факториала</a>и в коде, который на ней основан, не учитывается, что для 0 (нуля) тоже можно вычислить факториал - он равен 1 (единице). Исправленный код есть ниже в конспекте.</p>
1
<p>В видео есть неточность: в формуле вычисления<a>факториала</a>и в коде, который на ней основан, не учитывается, что для 0 (нуля) тоже можно вычислить факториал - он равен 1 (единице). Исправленный код есть ниже в конспекте.</p>
2
<h2>Транскрипт урока</h2>
2
<h2>Транскрипт урока</h2>
3
<p>У нас уже есть функция surfaceAreaCalculator, которая принимает один аргумент - радиус - и возвращает площадь поверхности соответствующей сферы, используя формулу 4 * pi * r^2. Помните, мы можем представить функции ящиками: кладем что-то в ящик, она производит какие-то действия и выплевывает результат.</p>
3
<p>У нас уже есть функция surfaceAreaCalculator, которая принимает один аргумент - радиус - и возвращает площадь поверхности соответствующей сферы, используя формулу 4 * pi * r^2. Помните, мы можем представить функции ящиками: кладем что-то в ящик, она производит какие-то действия и выплевывает результат.</p>
4
<p>Некоторые ящики не принимают ничего, другие ничего не выплевывают, а третьи вообще ничего не делают. Но мы сейчас заинтересованы в ящиках, подобных surfaceAreaCalculator, которая принимает что-то, вычисляет и возвращает результат.</p>
4
<p>Некоторые ящики не принимают ничего, другие ничего не выплевывают, а третьи вообще ничего не делают. Но мы сейчас заинтересованы в ящиках, подобных surfaceAreaCalculator, которая принимает что-то, вычисляет и возвращает результат.</p>
5
<p>Мы сделали эту функцию, чтобы упростить себе работу. Нам нужно вычислить площади поверхностей разных планет, а имея под рукой такую удобную функцию, нам не нужно помнить и переписывать формулу раз за разом.</p>
5
<p>Мы сделали эту функцию, чтобы упростить себе работу. Нам нужно вычислить площади поверхностей разных планет, а имея под рукой такую удобную функцию, нам не нужно помнить и переписывать формулу раз за разом.</p>
6
<p>Еще польза в том, что теперь код проще понять. Сравните это:</p>
6
<p>Еще польза в том, что теперь код проще понять. Сравните это:</p>
7
<p>с этим:</p>
7
<p>с этим:</p>
8
<p>Первый вариант намного приятней и проще, особенно для того, кто только что увидел этот код. Первый вариант отвечает на вопрос "что", второй - на вопрос "как".</p>
8
<p>Первый вариант намного приятней и проще, особенно для того, кто только что увидел этот код. Первый вариант отвечает на вопрос "что", второй - на вопрос "как".</p>
9
<p>Мы можем пойти дальше и собрать еще одну функцию - для вычисления квадратов. Давайте вначале взглянем на то, как мы можем ее использовать.</p>
9
<p>Мы можем пойти дальше и собрать еще одну функцию - для вычисления квадратов. Давайте вначале взглянем на то, как мы можем ее использовать.</p>
10
<p>Вместо умножения радиуса на радиус, мы вызовем функцию вычисления квадрата и передадим ей радиус. Очевидно - все, что делает функция вычисления квадрата, это "принимает число и возвращает его квадрат";</p>
10
<p>Вместо умножения радиуса на радиус, мы вызовем функцию вычисления квадрата и передадим ей радиус. Очевидно - все, что делает функция вычисления квадрата, это "принимает число и возвращает его квадрат";</p>
11
<p>Давайте отследим шаги и посмотрим, что происходит, когда мы запускаем нашу программу. Мы создаем константу surfaceOfMars и пытаемся сохранить в нее значение, которое возвращает функция surfaceAreaCalculator, когда она вызывается с числом 3390 в качестве аргумента.</p>
11
<p>Давайте отследим шаги и посмотрим, что происходит, когда мы запускаем нашу программу. Мы создаем константу surfaceOfMars и пытаемся сохранить в нее значение, которое возвращает функция surfaceAreaCalculator, когда она вызывается с числом 3390 в качестве аргумента.</p>
12
<p>3390 внутри функции известно как radius. Функция хочет умножить числа и выполнить возврат, но ей нужно знать последнее число, ей требуется вызвать функцию square и передать этот радиус. square принимает один аргумент - это число 3390, в нашем случае, и внутри функции square оно известно как num.</p>
12
<p>3390 внутри функции известно как radius. Функция хочет умножить числа и выполнить возврат, но ей нужно знать последнее число, ей требуется вызвать функцию square и передать этот радиус. square принимает один аргумент - это число 3390, в нашем случае, и внутри функции square оно известно как num.</p>
13
<p>square хочет умножить num на num и сделать возврат. Ей никто не мешает и она делает это умножение и возврат. Мы снова внутри surfaceAreaCalculator, который в прямом смысле ждал, пока функция square закончит свое дело. И теперь у нас есть результат вызова square. Он заменяет вызов, поэтому теперь становится возможным завершить умножение и вернуть ответ.</p>
13
<p>square хочет умножить num на num и сделать возврат. Ей никто не мешает и она делает это умножение и возврат. Мы снова внутри surfaceAreaCalculator, который в прямом смысле ждал, пока функция square закончит свое дело. И теперь у нас есть результат вызова square. Он заменяет вызов, поэтому теперь становится возможным завершить умножение и вернуть ответ.</p>
14
<p>Ответ возвращается и сохраняется в surfaceOfMars.</p>
14
<p>Ответ возвращается и сохраняется в surfaceOfMars.</p>
15
<p>Так что функции могут вызывать другие функции. Функция не знает и не напрягается, что она была вызвана другой функцией. Возможно, она была вызвана другой функцией, которая так же была вызвана еще какой-то функцией! Не так важно, при условии, что вычисление возвращается и заканчивает свою работу.</p>
15
<p>Так что функции могут вызывать другие функции. Функция не знает и не напрягается, что она была вызвана другой функцией. Возможно, она была вызвана другой функцией, которая так же была вызвана еще какой-то функцией! Не так важно, при условии, что вычисление возвращается и заканчивает свою работу.</p>
16
<p>Давайте попробуем еще поиграть с функциями, которые вызываются функциями. Допустим, у вас есть три книги на полке и вы хотите узнать, сколько есть возможных вариантов их перестановки.</p>
16
<p>Давайте попробуем еще поиграть с функциями, которые вызываются функциями. Допустим, у вас есть три книги на полке и вы хотите узнать, сколько есть возможных вариантов их перестановки.</p>
17
<p>Получается шесть уникальных комбинаций из трех книг. Из четырех - 24 комбинации. Из 13 - почти столько, сколько людей на планете. 25 книг? Вариантов их перестановки больше, чем атомов во Вселенной.</p>
17
<p>Получается шесть уникальных комбинаций из трех книг. Из четырех - 24 комбинации. Из 13 - почти столько, сколько людей на планете. 25 книг? Вариантов их перестановки больше, чем атомов во Вселенной.</p>
18
<p>Вообще, существует n! вариантов перестановки n книг. Факториал означает - умножить все целые числа от 1 до n. Так что, 3! - это 1 * 2 * 3. Давайте напишем функцию факториала.</p>
18
<p>Вообще, существует n! вариантов перестановки n книг. Факториал означает - умножить все целые числа от 1 до n. Так что, 3! - это 1 * 2 * 3. Давайте напишем функцию факториала.</p>
19
<p>Ой, подождите. Мы не знаем значение n изначально, в этом вся проблема. Хмм… Как там делается в математике?</p>
19
<p>Ой, подождите. Мы не знаем значение n изначально, в этом вся проблема. Хмм… Как там делается в математике?</p>
20
<p>А, хорошо, у них там есть два варианта: если n равно 0, тогда факториал - 1, это просто. Но если n не равно 0, тогда факториал - n*(n-1)!</p>
20
<p>А, хорошо, у них там есть два варианта: если n равно 0, тогда факториал - 1, это просто. Но если n не равно 0, тогда факториал - n*(n-1)!</p>
21
<p>Давайте попробуем вот так:</p>
21
<p>Давайте попробуем вот так:</p>
22
<p>Это может показаться странным. Мы вызываем функцию из функции, но… это та же самая функция!</p>
22
<p>Это может показаться странным. Мы вызываем функцию из функции, но… это та же самая функция!</p>
23
<p>Может тут что-то не так? Вообще-то нет! Все дело в том, что сама по себе функция - это не ящик, это его описание. Когда вы вызываете функцию, тогда создается ящик, а после того, как функция выполнилась, ящик самоуничтожается. Поэтому когда вы вызываете ту же самую функцию из нее самой, просто создается еще один ящик.</p>
23
<p>Может тут что-то не так? Вообще-то нет! Все дело в том, что сама по себе функция - это не ящик, это его описание. Когда вы вызываете функцию, тогда создается ящик, а после того, как функция выполнилась, ящик самоуничтожается. Поэтому когда вы вызываете ту же самую функцию из нее самой, просто создается еще один ящик.</p>
24
<p>Давайте это отследим: мы вызываем factorial(3). 3 это не 0, поэтому первое условие игнорируется. Функция хочет произвести умножение чисел и вернуть ответ, но она не может - ей нужно знать второе число в операции умножения. Для этого она вызывает factorial(3-1) или factorial(2).</p>
24
<p>Давайте это отследим: мы вызываем factorial(3). 3 это не 0, поэтому первое условие игнорируется. Функция хочет произвести умножение чисел и вернуть ответ, но она не может - ей нужно знать второе число в операции умножения. Для этого она вызывает factorial(3-1) или factorial(2).</p>
25
<p>Формируется новый идентичный ящик factorial, он принимает число 2, это не 0, так что он пробует произвести умножение и вернуть ответ, но не может - ему нужно знать второе число, поэтому он вызывает factorial(1).</p>
25
<p>Формируется новый идентичный ящик factorial, он принимает число 2, это не 0, так что он пробует произвести умножение и вернуть ответ, но не может - ему нужно знать второе число, поэтому он вызывает factorial(1).</p>
26
<p>Формируется новый идентичный ящик factorial, он принимает число 1 и это снова не 0. Еще одна попытка произвести умножение и вернуть результат, происходит вызов factorial(0) и этот ящик уже может мгновенно вернуть ответ - он возвращает 1.</p>
26
<p>Формируется новый идентичный ящик factorial, он принимает число 1 и это снова не 0. Еще одна попытка произвести умножение и вернуть результат, происходит вызов factorial(0) и этот ящик уже может мгновенно вернуть ответ - он возвращает 1.</p>
27
<p>1 возвращается в предыдущий ящик, умножается на 1 и ответ "1" возвращается в предыдущий ящик, умножается на 2 и ответ "2" возвращается в предыдущий ящик, умножается на 3 и ответ "6" возвращается во внешний мир и сохраняется в константе answer.</p>
27
<p>1 возвращается в предыдущий ящик, умножается на 1 и ответ "1" возвращается в предыдущий ящик, умножается на 2 и ответ "2" возвращается в предыдущий ящик, умножается на 3 и ответ "6" возвращается во внешний мир и сохраняется в константе answer.</p>
28
<p>Фуух!</p>
28
<p>Фуух!</p>
29
<p>Все это и есть рекурсия: что-то описывается через самого себя, содержит себя в своем описании. Когда дело касается математики или программирования, требуется два условия:</p>
29
<p>Все это и есть рекурсия: что-то описывается через самого себя, содержит себя в своем описании. Когда дело касается математики или программирования, требуется два условия:</p>
30
<ol><li>Простой базовый случай или терминальный сценарий. Это точка, в которой нужно остановиться. В нашем примере это 0: мы остановили вычисление факториала когда в функцию был передан 0.</li>
30
<ol><li>Простой базовый случай или терминальный сценарий. Это точка, в которой нужно остановиться. В нашем примере это 0: мы остановили вычисление факториала когда в функцию был передан 0.</li>
31
<li>Правило передвижения по рекурсии, углубление. В нашем случае это было n * factorial(n-1).</li>
31
<li>Правило передвижения по рекурсии, углубление. В нашем случае это было n * factorial(n-1).</li>
32
</ol><p>Еще один момент. Если проверить наш код с помощью линтера, то он выдаст ошибку no-else-return. Последуем рекомендациями линтера и отрефакторим код:</p>
32
</ol><p>Еще один момент. Если проверить наш код с помощью линтера, то он выдаст ошибку no-else-return. Последуем рекомендациями линтера и отрефакторим код:</p>
33
<p>Давайте проследим шаги еще раз, но с другой точки зрения, не заглядывая в ящики. Вот, как это выглядит пошагово:</p>
33
<p>Давайте проследим шаги еще раз, но с другой точки зрения, не заглядывая в ящики. Вот, как это выглядит пошагово:</p>
34
<p>Умножение не происходит пока мы спускаемся до базового случая функции factorial(0). А затем мы возвращаемся наверх, производя одно умножение за один шаг.</p>
34
<p>Умножение не происходит пока мы спускаемся до базового случая функции factorial(0). А затем мы возвращаемся наверх, производя одно умножение за один шаг.</p>
35
<p>Рекурсия широко используется, особенно в функциональном программировании - одном из стилей программирования. И не только для математических вычислений, а для множества других процессов!</p>
35
<p>Рекурсия широко используется, особенно в функциональном программировании - одном из стилей программирования. И не только для математических вычислений, а для множества других процессов!</p>
36
<p>Иногда информация в компьютере по своей природе требует рекурсивных функций. Например, веб-страницы состоят из HTML-элементов, и одни элементы могут входить в другие. Теги в тегах в тегах. И для эффективной обработки страницы браузеру требуется рекурсивно двигаться от уровня к уровню чтобы понять, в каком именно виде нужно вывести эти элементы на экран для пользователя.</p>
36
<p>Иногда информация в компьютере по своей природе требует рекурсивных функций. Например, веб-страницы состоят из HTML-элементов, и одни элементы могут входить в другие. Теги в тегах в тегах. И для эффективной обработки страницы браузеру требуется рекурсивно двигаться от уровня к уровню чтобы понять, в каком именно виде нужно вывести эти элементы на экран для пользователя.</p>
37
<p>Вы будете постоянно сталкиваться с рекурсией в этом и последующих курсах, потому что это невероятно мощная штука и, должен признаться, довольно крутая.</p>
37
<p>Вы будете постоянно сталкиваться с рекурсией в этом и последующих курсах, потому что это невероятно мощная штука и, должен признаться, довольно крутая.</p>
38
<p>Ваша очередь. Переходите к тестам и упражнениям, создайте свою рекурсивную функцию. Процесс может оказаться немного каверзным, но помните: вам нужно описать две вещи - как углубляться и когда остановиться. Удачи!</p>
38
<p>Ваша очередь. Переходите к тестам и упражнениям, создайте свою рекурсивную функцию. Процесс может оказаться немного каверзным, но помните: вам нужно описать две вещи - как углубляться и когда остановиться. Удачи!</p>
39
<h2>Выводы</h2>
39
<h2>Выводы</h2>
40
<h3>О функциях</h3>
40
<h3>О функциях</h3>
41
<ul><li>Можно представить функции как черные коробки: коробка забирает объект, производит внутри какие-то действия, а потом выплевывает что-то новое<ul><li>Некоторые функции ничего не забирают (не принимают аргументы), некоторые вообще ничего не делают (они пустые), некоторые не возвращают значения.</li>
41
<ul><li>Можно представить функции как черные коробки: коробка забирает объект, производит внутри какие-то действия, а потом выплевывает что-то новое<ul><li>Некоторые функции ничего не забирают (не принимают аргументы), некоторые вообще ничего не делают (они пустые), некоторые не возвращают значения.</li>
42
<li>Наш surfaceAreaCalculator принимает один аргумент (radius), вычисляет площадь поверхности и возвращает результат этого вычисления.</li>
42
<li>Наш surfaceAreaCalculator принимает один аргумент (radius), вычисляет площадь поверхности и возвращает результат этого вычисления.</li>
43
</ul></li>
43
</ul></li>
44
<li>Функции могут вызывать другие функции</li>
44
<li>Функции могут вызывать другие функции</li>
45
<li>surfaceAreaCalculator может вызывать функцию square, чтобы получить радиус, возведенный в квадрат, вместо того, чтобы умножать радиус на радиус.</li>
45
<li>surfaceAreaCalculator может вызывать функцию square, чтобы получить радиус, возведенный в квадрат, вместо того, чтобы умножать радиус на радиус.</li>
46
<li>Мы пишем функции, чтобы облегчить жизнь:<ul><li>такой код легче понимать</li>
46
<li>Мы пишем функции, чтобы облегчить жизнь:<ul><li>такой код легче понимать</li>
47
<li>функции могут переиспользоваться несколько раз</li>
47
<li>функции могут переиспользоваться несколько раз</li>
48
</ul></li>
48
</ul></li>
49
</ul><p>Сравните:</p>
49
</ul><p>Сравните:</p>
50
<h3>Две функции вместе</h3>
50
<h3>Две функции вместе</h3>
51
<h3>Функции, которые вызывают сами себя</h3>
51
<h3>Функции, которые вызывают сами себя</h3>
52
<ul><li><strong>Определение функции</strong>- это описание коробки</li>
52
<ul><li><strong>Определение функции</strong>- это описание коробки</li>
53
<li>Оригинал коробки формируется при<strong>вызове функции</strong></li>
53
<li>Оригинал коробки формируется при<strong>вызове функции</strong></li>
54
<li>Когда функция вызывает сама себя, создается новая идентичная коробка</li>
54
<li>Когда функция вызывает сама себя, создается новая идентичная коробка</li>
55
</ul><p>Перестановки:</p>
55
</ul><p>Перестановки:</p>
56
<ul><li>Количество способов перестановки n объектов равно n! (<a>перестановки</a>)</li>
56
<ul><li>Количество способов перестановки n объектов равно n! (<a>перестановки</a>)</li>
57
<li>n! определяется таким способом: если n = 0, то<strong>n! = 1</strong>; если n > 0, то<strong>n! = n * (n-1)!</strong></li>
57
<li>n! определяется таким способом: если n = 0, то<strong>n! = 1</strong>; если n > 0, то<strong>n! = n * (n-1)!</strong></li>
58
</ul><p>Функция, вычисляющая факториал:</p>
58
</ul><p>Функция, вычисляющая факториал:</p>
59
<h3>Требования рекурсии</h3>
59
<h3>Требования рекурсии</h3>
60
<ol><li>Простой базовый случай, или терминальный сценарий, или терминальное условие. Простыми словами, когда остановиться. В нашем примере это был 0: мы остановили вычисление факториала, когда достигли 0.</li>
60
<ol><li>Простой базовый случай, или терминальный сценарий, или терминальное условие. Простыми словами, когда остановиться. В нашем примере это был 0: мы остановили вычисление факториала, когда достигли 0.</li>
61
<li>Правило передвижения по рекурсии, углубление. В нашем случае, это было n * factorial(n-1).</li>
61
<li>Правило передвижения по рекурсии, углубление. В нашем случае, это было n * factorial(n-1).</li>
62
</ol><h3>Ожидание умножения</h3>
62
</ol><h3>Ожидание умножения</h3>
63
<p>Ничего не умножается, пока мы спускаемся к базовому случаю factorial(0). Затем мы начинаем подниматься обратно, по одному шагу.</p>
63
<p>Ничего не умножается, пока мы спускаемся к базовому случаю factorial(0). Затем мы начинаем подниматься обратно, по одному шагу.</p>
64
<h3>Примечание</h3>
64
<h3>Примечание</h3>
65
<p>Заметьте, что 0! это 1, а простой базовый случай для n! это 0! В этом уроке мы пропустили такой случай, чтобы сократить рекурсию на один вызов и на одну коробку, поскольку 1 * 1 - это, в любом случае - 1.</p>
65
<p>Заметьте, что 0! это 1, а простой базовый случай для n! это 0! В этом уроке мы пропустили такой случай, чтобы сократить рекурсию на один вызов и на одну коробку, поскольку 1 * 1 - это, в любом случае - 1.</p>
66
<h3>Просто ради забавы</h3>
66
<h3>Просто ради забавы</h3>
67
<p>У программистов есть одна шутка: "Чтобы понять рекурсию, нужно понять рекурсию". Google, кажется, любит такие шутки. Попробуйте погуглить "рекурсия" и зацените верхний результат поиска ;-)</p>
67
<p>У программистов есть одна шутка: "Чтобы понять рекурсию, нужно понять рекурсию". Google, кажется, любит такие шутки. Попробуйте погуглить "рекурсия" и зацените верхний результат поиска ;-)</p>