HTML Diff
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 &gt; 0, то<strong>n! = n * (n-1)!</strong></li>
57 <li>n! определяется таким способом: если n = 0, то<strong>n! = 1</strong>; если n &gt; 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>