From 3ce9be51f3ac8d5099c1e99b7b1db01430dcbe47 Mon Sep 17 00:00:00 2001 From: Andrii Hrushetskyi Date: Wed, 19 Feb 2025 15:02:44 +0100 Subject: [PATCH] fixed typos and simplified explanation **In chapter 01-06-01 recursion** Some of sentences were correct, but they were "unnatural" in Ukrainian and make understanding more difficult. --- .../01-recursion/article.md | 78 +++++++++---------- .../01-recursion/recursion-pow.svg | 2 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index 22dac9307..8d2aa0cc2 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -8,11 +8,11 @@ Рекурсія -- це паттерн, який є корисним у ситуаціях, коли завдання може бути розділено на кілька завдань того ж роду, але простіших. Або коли завдання може бути спрощене до простої дії плюс простіший варіант того ж завдання. Або, як ми побачимо найближчим часом, щоб працювати з певними структурами даних. -Коли функція вирішує завдання, у процесі вона може викликати багато інших функцій. Частковий випадок цього є те, коли функція викликає *себе*. Це називається *рекурсія*. +Коли функція вирішує завдання, у процесі вона може викликати багато інших функцій. Є навіть випадки, коли функція викликає *саму себе*. Коли функція викликає саму себе - це називається *рекурсія*. ## Два способи мислення -Щоб почати з чогось простого -- давайте напишемо функцію `pow(x, n)`, що приводить `x` в натуральну степінь `n`. Іншими словами, множить `x` сам на себе `n` разів. +Щоб почати з чогось простого -- давайте напишемо функцію `pow(x, n)`, що підносить `x` до натурального ступеня `n`. Іншими словами, множить `x` саму на себе `n` разів. ```js pow(2, 2) = 4 @@ -53,9 +53,9 @@ pow(2, 4) = 16 alert( pow(2, 3) ); // 8 ``` -Зверніть увагу, як рекурсивний варіант принципово відрізняється. +Зверніть увагу, що рекурсивний варіант відрізняється принципово. -Коли `pow(x, n)` викликається, виконання розділяється на дві гілки: +Коли функція `pow(x, n)` викликається, виконання розділяється на дві гілки: ```js if n==1 = x @@ -65,25 +65,25 @@ pow(x, n) = else = x * pow(x, n - 1) ``` -1. Якщо `n == 1`, то все тривіально. Це називається *база* рекурсії, оскільки вона негайно виробляє очевидний результат: `pow(x, 1)` дорівнює `x`. -2. Інакше ми можемо представляти `pow(x, n)` як `x * pow(x, n)`. У математиці можна написати xn = x * xn-1. Це називається *рекурсивний крок*: ми перетворюємо завдання на простішу дію (множення за допомогою `x`) та на простий виклик того ж завдання (`pow` з меншим `n`). Наступні кроки спрощують його далі і далі до `n`, що дорівнює `1`. +1. Якщо `n == 1`, то все просто. Ця гілка називається *базою* рекурсії, оскільки вона негайно дає очевидний результат: `pow(x, 1)` дорівнює `x`. +2. Інакше ми можемо представити `pow(x, n)` як `x * pow(x, n)`. Що у математиці можна написати xn = x * xn-1. Ця гілка - *крок рекурсії*: ми зводимо задачу до більш простої дії (множеня на `x`) і до більш простої аналогічної задачі (`pow` з меншим `n`). Наступні кроки спрощують задачу все більше і більше, аж доки `n` почне дорівнювати `1`. Ми також можемо сказати, що `pow` *рекурсивно викликає себе* до`n == 1`. ![рекурсивна діаграма pow](recursion-pow.svg) -Наприклад, для розрахунку `pow(2, 4)` рекурсивний варіант виконує ці кроки: +Наприклад, для розрахунку `pow(2, 4)` рекурсивний варіант виконує такі кроки: 1. `pow(2, 4) = 2 * pow(2, 3)` 2. `pow(2, 3) = 2 * pow(2, 2)` 3. `pow(2, 2) = 2 * pow(2, 1)` 4. `pow(2, 1) = 2` -Отже, рекурсія робить виклик функції простішим, а потім -- ще більш простішим, і так далі, доки результат стане очевидним. +Отже, рекурсію використовують, коли обчислення функції можна звести до її ж більш простого виклику, а той - до ще простішого і так далі, аж доки значення не стане максимально простим (очевидним). -````smart header="Рекурсія зазвичай коротша" -Рекурсивне рішення, як правило, коротше, ніж ітераційне. +````smart header="Рекурсійне рішення зазвичай коротше" +Рекурсивне рішення, як правило, коротше за ітераційне. Ми можемо переписати те ж саме, використовуючи умовний оператор `?` замість `if`, щоб зробити `pow(x, n)` більш лаконічним і зберегти легкість читання: @@ -96,7 +96,7 @@ function pow(x, n) { Максимальна кількість вкладених викликів (включаючи перший) називається *глибина рекурсії*. У нашому випадку вона буде точно дорівнювати `n`. -Максимальна глибина рекурсії обмежена рушієм JavaScript. Ми можемо покластися, що вона може дорівнювати 10000, деякі рушії дозволяють отримати більшу глибину, але 100000, ймовірно, не підтримується більшістю з них. Є автоматичні оптимізації, які допомагають пом’якшити це ("оптимізація хвостових викликів"), але вони ще не підтримуються скрізь і працюють лише у простих випадках. +Максимальна глибина рекурсії обмежена рушієм JavaScript. Кількість вкладених викликів, на яку точно можна розраховувати - 10 000. Деякі рушії дозволять працювати з ще більшою глибиною. Але навіть більшість з них можуть осилити не більше 100 000 викликів. Є багато автоматичних оптимізацій, які допомагають уникнути переповнення стеку викликів ("оптимізація хвостових викликів", англ. tail calls optimizations), але вони підтримуються далеко не всюди і працюють тільки в простих випадках. Це обмежує застосування рекурсії, але вона все ще залишається дуже широко поширеною. Є багато завдань, де рекурсивний спосіб мислення дає простіший код, який легше підтримувати. @@ -114,7 +114,7 @@ function pow(x, n) { - Поточна функція зупиняється. - Контекст виконання, пов’язаний з нею, запам’ятовується в спеціальній структурі даних, що називається *стек контекстів виконання*. -- Вкладений виклик виконується. +- Виконуються вкладені виклики і для кожного з них створюється свій контекст виконання. - Після закінчення, старий контекст виконання витягується з стека, і зовнішня функція відновлюється з того місця, де вона зупинилася. Давайте подивимося, що відбувається під час виклику `pow(2, 3)`. @@ -132,7 +132,7 @@ function pow(x, n) { -Ось тоді, функція починає виконуватися. Умова `n == 1` -- хибна, тому потік продовжується у другій гілці `if`: +Це стан на початку виконання. Так як умова `n == 1` -- хибна, то виконується друга гілка `if`: ```js run function pow(x, n) { @@ -158,17 +158,17 @@ alert( pow(2, 3) ); -Для розрахунку `x * pow(x, n - 1)`, ми повинні зробити підвиклик `pow` з новими аргументами `pow(2, 2)`. +Для розрахунку `x * pow(x, n - 1)` треба зробити підвиклик `pow` з новими аргументами `pow(2, 2)`. ### pow(2, 2) -Щоб зробити вкладений виклик, JavaScript пам’ятає контекст поточного виконання в *стеці контексту виконання*. +Щоб зробити вкладений виклик, JavaScript запам'ятовує контекст поточного виконання в *стеці контексту виконання*. Тут ми викликаємо ту ж функцію `pow`, але це абсолютно не має значення. Цей процес однаковий для всіх функцій: 1. Поточний контекст "запам’ятовується" на вершині стека. -2. Новий контекст створюється для підвиклику. -3. Коли закінчиться підвиклик -- попередній контекст дістається зі стека, і його виконання продовжується. +2. Створюється новий контекст для вкладеного виклику. +3. Коли закінчиться виконання вкладеного виклику -- попередній контекст дістається зі стека, і його виконання продовжується. Ось контекстний стек, коли ми увійшли до підвиклику `pow(2, 2)`: @@ -183,9 +183,9 @@ alert( pow(2, 3) ); -Новий поточний контекст виконання знаходиться на вершині (виділений жирним шрифтом), а попередні контексти знаходяться в пам’яті нижче. +Новий поточний контекст виконання знаходиться на малюнку вище (підпис виділений жирним шрифтом), а попередні контексти, які все ще бережуть в пам'яті, зображені на тому ж малюнку нижче. -Коли ми закінчимо підвиклик, легко відновити попередній контекст, оскільки він зберігає як змінні, так і точне місце коду, де він зупинився. +Коли виконання підвиклику закінчиться, можна буде легко повернутись назад. Бо контекст зберігає як значення змінних, так і точне місце в коді, де він зупинився. ```smart Тут, на малюнку, ми використовуємо "на рядку", так як у нашому прикладі є лише один підвиклик в рядку, але, як правило, один рядок коду може містити декілька підвикликів, як `pow(…) + pow(…) + somethingElse(…)`. @@ -197,7 +197,7 @@ alert( pow(2, 3) ); Процес повторюється: новий підвиклик здійснюється на рядку `5`, тепер з аргументами `x=2`, `n=1`. -Створено новий контекст виконання, попередній витиснуто на вершину стека: +Створено новий контекст виконання, попередній витиснуто вище по стеку: