Recursion and stack by MykolaSopiha · Pull Request #201 · javascript-tutorial/uk.javascript.info · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions 1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md
22 changes: 11 additions & 11 deletions 1-js/06-advanced-functions/01-recursion/01-sum-to/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Sum all numbers till the given one
# Сума всіх чисел до даного

Write a function `sumTo(n)` that calculates the sum of numbers `1 + 2 + ... + n`.
Напишіть функцію `sumTo(n)`, що обчислює суму чисел `1 + 2 + ... + n`.

For instance:
Наприклад:

```js no-beautify
sumTo(1) = 1
Expand All @@ -17,20 +17,20 @@ sumTo(4) = 4 + 3 + 2 + 1 = 10
sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
```

Make 3 solution variants:
Зробити 3 варіанти рішення:

1. Using a for loop.
2. Using a recursion, cause `sumTo(n) = n + sumTo(n-1)` for `n > 1`.
3. Using the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) formula.
1. Використання циклу.
2. Використання рекурсії, у випадку `sumTo(n) = n + sumTo(n-1)` для `n > 1`.
3. Використання формули [арифметичної прогресії] (https://uk.wikipedia.org/wiki/Арифметична_прогресія).

An example of the result:
Приклад результату:

```js
function sumTo(n) { /*... your code ... */ }
function sumTo(n) { /*... ваш код ... */ }

alert( sumTo(100) ); // 5050
```

P.S. Which solution variant is the fastest? The slowest? Why?
P.S. Який варіант рішення є найшвидшим? Найповільнішим? Чому?

P.P.S. Can we use recursion to count `sumTo(100000)`?
P.P.S. Чи можемо ми використовувати рекурсію для підрахунку `sumTo(100000)`?
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
By definition, a factorial `n!` can be written as `n * (n-1)!`.
За визначенням, факторіал `n!` може бути записаний як `n * (n-1)!`.

In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`.
Інакше кажучи, результат `factorial(n)` може бути розрахований, як `n` помножений на результат `factorial(n-1)`. І виклик до `n-1` може рекурсивно спускатися нижче та нижче, аж до `1`.

```js run
function factorial(n) {
Expand All @@ -10,7 +10,7 @@ function factorial(n) {
alert( factorial(5) ); // 120
```

The basis of recursion is the value `1`. We can also make `0` the basis here, doesn't matter much, but gives one more recursive step:
Базисом рекурсії є значення `1`. Ми також можемо зробити `0` базисом, це не має великого значення, але дає ще один рекурсивний крок:

```js run
function factorial(n) {
Expand Down
12 changes: 6 additions & 6 deletions 1-js/06-advanced-functions/01-recursion/02-factorial/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ importance: 4

---

# Calculate factorial
# Розрахувати факторіал

The [factorial](https://en.wikipedia.org/wiki/Factorial) of a natural number is a number multiplied by `"number minus one"`, then by `"number minus two"`, and so on till `1`. The factorial of `n` is denoted as `n!`
[Факторіал](https://uk.wikipedia.org/wiki/Факторіал) з натурального числа -- це число, помножене на `"число мінус один"`, потім на `"число мінус два"` і так до `1`. Факторіал `n` позначається як `n!`

We can write a definition of factorial like this:
Ми можемо написати визначення факторіалу наступним чином:

```js
n! = n * (n - 1) * (n - 2) * ...*1
```

Values of factorials for different `n`:
Значення факторіалів для різних `n`:

```js
1! = 1
Expand All @@ -22,10 +22,10 @@ Values of factorials for different `n`:
5! = 5 * 4 * 3 * 2 * 1 = 120
```

The task is to write a function `factorial(n)` that calculates `n!` using recursive calls.
Завдання полягає в тому, щоб написати функцію `factorial(n)`, яка обчислює `n!` за допомогою рекурсивних викликів.

```js
alert( factorial(5) ); // 120
```

P.S. Hint: `n!` can be written as `n * (n-1)!` For instance: `3! = 3*2! = 3*2*1! = 6`
P.S. Підказка: `n!` може бути записане як `n * (n-1)!`. Наприклад: `3! = 3*2! = 3*2*1! = 6`
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Loop-based solution
# Рішення на основі циклу

The loop-based variant of the solution:
Варіант рішення на основі циклу:

```js run
let list = {
Expand Down Expand Up @@ -30,7 +30,7 @@ function printList(list) {
printList(list);
```

Please note that we use a temporary variable `tmp` to walk over the list. Technically, we could use a function parameter `list` instead:
Зверніть увагу, що ми використовуємо тимчасову змінну `tmp`, щоб пройти по списку. Технічно ми могли б використовувати замість нього параметр функції `list`:

```js
function printList(list) {
Expand All @@ -43,15 +43,15 @@ function printList(list) {
}
```

...But that would be unwise. In the future we may need to extend a function, do something else with the list. If we change `list`, then we lose such ability.
...Але це було б нерозумно. У майбутньому нам доведеться розширити функцію, зробити щось інше з `list`. Якщо ми змінюємо `list`, то ми втрачаємо таку здатність.

Talking about good variable names, `list` here is the list itself. The first element of it. And it should remain like that. That's clear and reliable.
Говорячи про хороші імена змінних, `list` тут -- це сам список. Його перший елемент. І це повинно залишитися так. Це ясно і надійний.

From the other side, the role of `tmp` is exclusively a list traversal, like `i` in the `for` loop.
З іншого боку, `tmp` використовується виключно проходу, як `i` у `for` циклі.

# Recursive solution
# Рішення через рекурсію

The recursive variant of `printList(list)` follows a simple logic: to output a list we should output the current element `list`, then do the same for `list.next`:
Рекурсивний варіант `printlist(list)` слідує простій логіці: вивести список, який ми повинні вивести поточний елемент `list`, а потім зробити те ж саме для` list.next`:

```js run
let list = {
Expand All @@ -70,19 +70,19 @@ let list = {

function printList(list) {

alert(list.value); // output the current item
alert(list.value); // виведіть поточний елемент

if (list.next) {
printList(list.next); // do the same for the rest of the list
printList(list.next); // зробіть те ж саме для решти списку
}

}

printList(list);
```

Now what's better?
Що ж тепер краще?

Technically, the loop is more effective. These two variants do the same, but the loop does not spend resources for nested function calls.
Технічно цикл є більш ефективним. Ці два варіанти роблять те ж саме, але цикл не витрачає ресурси для вкладених викликів.

From the other side, the recursive variant is shorter and sometimes easier to understand.
З іншого боку, рекурсивний варіант коротший, а іноді його легше зрозуміти.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 5

---

# Output a single-linked list
# Вивести одинозв’язаний список

Let's say we have a single-linked list (as described in the chapter <info:recursion>):
Скажімо, у нас є одинозв’язаний список (як описано в розділі <info:recursion>):

```js
let list = {
Expand All @@ -22,8 +22,8 @@ let list = {
};
```

Write a function `printList(list)` that outputs list items one-by-one.
Напишіть функцію `printList(list)`, що виводить список елементів один за одним.

Make two variants of the solution: using a loop and using recursion.
Зробіть два варіанти рішення: з використанням циклу та з використанням рекурсії.

What's better: with recursion or without it?
Що краще: з рекурсією чи без неї?
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Using a recursion
# Використання рекурсії

The recursive logic is a little bit tricky here.
Тут рекурсивна логіка трохи складна.

We need to first output the rest of the list and *then* output the current one:
Нам потрібно спочатку вивести останні елементи списку, а *потім* вивести поточний:

```js run
let list = {
Expand Down Expand Up @@ -31,13 +31,13 @@ function printReverseList(list) {
printReverseList(list);
```

# Using a loop
# За допомогою циклу

The loop variant is also a little bit more complicated then the direct output.
Варіант циклу також трохи складніше, ніж прямий вивід.

There is no way to get the last value in our `list`. We also can't "go back".
Немає можливості отримати останнє значення в нашому `list`. Ми також не можемо "повернутися назад".

So what we can do is to first go through the items in the direct order and remember them in an array, and then output what we remembered in the reverse order:
Отже, що ми можемо зробити, так це спочатку пройти елементи в прямому порядку і запам’ятати їх у масиві, а потім вивести те, що ми запам’ятали, в зворотному порядку:

```js run
let list = {
Expand Down Expand Up @@ -71,4 +71,4 @@ function printReverseList(list) {
printReverseList(list);
```

Please note that the recursive solution actually does exactly the same: it follows the list, remembers the items in the chain of nested calls (in the execution context stack), and then outputs them.
Зверніть увагу, що рекурсивне рішення фактично робить точно так само: проходиться списком, запам’ятовує елементи в ланцюжку вкладених викликів (у контекстному стеку виконання), а потім виводить їх.
Loading