You are on page 1of 55

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ
УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ

МЕТОДИЧНІ ВКАЗІВКИ
до практичних занять з дисципліни
«ОСНОВИ ПРОГРАМУВАННЯ»

Харків 2022
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ
УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ

МЕТОДИЧНІ ВКАЗІВКИ
до практичних занять з дисципліни

«ОСНОВИ ПРОГРАМУВАННЯ»
для студентів першого (бакалаврського) рівня вищої освіти
усіх форм навчання
спеціальності 121 – «Інженерія програмного забезпечення»
освітньо-професійна програма «Програмна інженерія»

ЗАТВЕРДЖЕНО
кафедрою ПІ.
Протокол № 3 від 26.09.2022.

Харків 2022

2
Методичні вказівки до практичних занять з дисципліни «Основи
програмування» для студентів першого (бакалаврського) рівня вищої освіти усіх
форм навчання спеціальності 121 «Інженерія програмного забезпечення»,
освітньо-професійна програма «Програмна інженерія»/ Упоряд.: В.М.Бондарєв,
Ю.Ю.Черепанова. – Харків: ХНУРЕ, 2022. – 55 с.

Упорядники: В.М. Бондарєв,


Ю.Ю. Черепанова

Рецензент

3
ЗМІСТ

Вступ ......................................................................................................................6
1 Команди умовна та повторення .......................................................................7
1.1 Мета роботи ................................................................................................7
1.2 Методичні вказівки з організації самостійної роботи студентів ..........7
1.3 Приклади аудиторних і домашніх задач ..................................................12
2 Функції ...............................................................................................................13
2.1 Мета роботи ...............................................................................................13
2.2 Методичні вказівки з організації самостійної роботи студентів ..........13
2.3 Приклади аудиторних і домашніх задач ..................................................16
3 Сторінки HTML .................................................................................................17
3.1 Мета роботи ................................................................................................17
3.2 Методичні вказівки з організації самостійної роботи студентів ..........17
3.3 Приклади аудиторних і домашніх задач ..................................................22
4 Масиви ................................................................................................................22
4.1 Мета роботи ...............................................................................................22
4.2 Методичні вказівки з організації самостійної роботи студентів .........22
4.3 Приклади аудиторних і домашніх задач ..................................................26
5 Середовище розробки .......................................................................................27
5.1 Мета роботи ................................................................................................27
5.2 Методичні вказівки з організації самостійної роботи студентів ..........27
5.3 Приклади аудиторних і домашніх задач ..................................................30
6 Рядки ...................................................................................................................30
6.1 Мета роботи ................................................................................................30
6.2 Методичні вказівки з організації самостійної роботи студентів ..........30
6.3 Приклади аудиторних і домашніх задач ..................................................33
7 Посилальні типи ................................................................................................34
7.1 Мета роботи ................................................................................................34
7.2 Методичні вказівки з організації самостійної роботи студентів ..........34
7.3 Приклади аудиторних і домашніх задач ..................................................37

4
8 Об’єкти ...............................................................................................................38
8.1 Мета роботи ................................................................................................38
8.2 Методичні вказівки з організації самостійної роботи студентів ..........38
8.3 Приклади аудиторних і домашніх задач ..................................................44
9 Рекурсія ..............................................................................................................44
9.1 Мета роботи ................................................................................................44
9.2 Методичні вказівки з організації самостійної роботи студентів ..........44
9.3 Приклади аудиторних і домашніх задач ..................................................48
10 Функціональне програмування ......................................................................49
10.1 Мета роботи ..............................................................................................49
10.2 Методичні вказівки з організації самостійної роботи студентів ........49
10.3 Приклади аудиторних і домашніх задач ................................................52
Перелік джерел посилання ..................................................................................54

5
ВСТУП

Навчати програмуванню неможливе без активної практики з боку студен-


тів, і мета цих методичних вказівок – саме забезпечити студентів такою практи-
кою.
Вказівки мають десять розділів, по кількості практичних занять в учбовому
плані. Кожен розділ починається з посилань на ті глави учбового посібника, які
студент має оновити в пам’яті перед заняттям. Далі в розділі надається декілька
прикладів вирішення типових задач. Закінчується розділ задачами, які студенти
повинні вирішувати самостійно.
Обрані викладачем студентські рішення мають стати предметом аналізу і
обговорювання з аудиторією на даному або наступному занятті.
Заняття «Сторінки HTML» і «Середовища розробки» не мають тісного
зв’язку з лекційним матеріалом, мінімально необхідний об’єм знань викладений
безпосередньо в розділах.
Програмний код в тексті вказівок буде виділятися моноширинним шри-
фтом.
console.log(1);
Куточок на початку рядка означає, що команда вводиться в консолі.
> console.log(2);
2
У наступному рядку зазвичай буде відповідь інтерпретатора.
Скорочення JS означає javascript.

6
1 КОМАНДИ УМОВНА ТА ПОВТОРЕННЯ

1.1 Мета заняття

Навчитися організовувати повторення у програмі за допомогою циклів.

1.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
03 Команда присвоювання;
04 Умовна команда;
05 Блок команд;
06 Команда повторення.

Розглянемо типову задачу на повторення, і простежмо послідовність дій по


її вирішенню.
Задача «Друк чисел»
Роздрукувати натуральні числа, які не перевищують заданого числа n.
Вирішення задачі
Як відомо, натуральними звуться цілі додатні числа, більші за 0, тобто
1,2,3, ... ∞. В задачі встановлена межа – число n, і тому нам потрібні не всі, а лише
частина натуральних чисел – 1, 2, ... n – 1, n.
В задачі не сказано, в якій послідовності слід друкувати числа, тому ми
можемо самі обрати цю послідовність, скажімо, 1, 2, 3, ... n.
Виконувати програму будемо у веб-браузері, а «друкувати» числа будемо
шляхом виводу їх на консоль браузера, бо вивід у вікно alert буде нас втомлю-
вати.
Програма повинна мати параметр, який зробіть її придатною для друку до-
вільної кількості чисел без суттєвою зміни її коду. Як це зробити правильно, ми

7
незабаром дізнаємося, а поки роль параметру хай відіграє змінна n, якій будем
надавати певні значення перед використанням програми.
let n = 10;
Вочевидь, нашу задачу неможливо вирішити, просто написавши команду
consile.log декілька разів,
console.log(1);
console.log(2);
console.log(3);
тому, що кількість повторень задається параметром.
Потрібно завести окрему змінну для представлення числа, що друкується,
скажімо x, і повторювати команду
console.log(x);
надаючи змінній x послідовно значення 1, 2, 3,..., n.
let n = 10; // це параметр програми
let x = 1;
console.log(x);
x = x + 1;

console.log(x);
x = x + 1;

console.log(x);
x = x + 1;
...
Звісно, це так само не вирішує задачу, але тут добре видно ті команди, що
повторюються:
console.log(x);
x = x + 1;
Повторення мають продовжуватися, доки x не перевищить n, тобто, умова
повторення це порівняння x <= n.
Тепер ми готові записати остаточне рішення.

8
let n = 10;
let x = 1;
while (x <= n) {
console.log(x);
x = x + 1;
}
Зауваження. Зазвичай імена змінних повинні відображувати їх призна-
чення, але в даному випадку ніякого особливого призначення число x не має,
тому і назва в нього невиразна.
До речі, без змінної x взагалі можна обійтися. Її роль може взяти на себе
змінна n, хоча числа будуть надруковані за зменшенням.
let n = 10;
while (n > 0) {
console.log(n);
n = n - 1;
}
Задача «3 або 5»
Підрахувати, скільки натуральних чисел, які не перевищують заданого чи-
сла n, діляться без остачі на 3 або на 5.
Вирішення задачі
По-перше, як встановити, чи ділиться x на y без остачі? На це здатна ариф-
метична операція % – взяття остачі від ділення. Якщо вираз x % y дорівнює нулю,
то x ділиться на y без остачі. Спробуйте самі у консолі браузера.
> 13 % 3 == 0
false
> 12 % 3 == 0
true
В цій задачі є щось подібне до попередньої, а саме, натуральні числа, які не
перевищують n. Тому візьмемо за основу попереднє рішення.
let n = 10;
while (n > 0) {

9
console.log(n);
n = n - 1;
}
Поки що почекаємо із підрахунком, будемо числа не рахувати, а друкувати,
але не всі, а вибірково.
let n = 10;
while (n > 0) {
// друкуємо ті, що діляться на 3
if (n % 3 == 0)
console.log(n)
// друкуємо ті, що діляться на 5
if (n % 5 == 0)
console.log(n)
n = n - 1;
}
Така програма надрукує числа: 10, 9, 6, 5 і 3. Начебто, все гаразд і можна
переходити до підрахунку.
Додамо до програми ще одну змінну, count – лічильник. Спочатку занесемо
в неї 0, а потім будемо нарощувати на одиницю, коли зустрінеться підходяще
число. По завершенні циклу значення змінної count покажемо користувачу.

let n = 10;
let count = 0;
while (n > 0) {
// рахуємо ті, що діляться на 3
if (n % 3 == 0)
count = count + 1
// рахуємо ті, що діляться на 5
if (n % 5 == 0)
count = count + 1
n = n - 1;

10
}
console.log(count);
Користувач побачить 5, що відповідає нашим попереднім спостереженням
- 10, 9, 6, 5 і 3. Про всяк випадок перевіримо програму ще раз, для n = 20. Відпо-
відь має бути 9, бо таких чисел дійсно дев’ять: 3, 5, 6, 9, 10, 12, 15, 18, 20.
Але відповідь нашої програми – 10, у чому справа? Повернімо друк замість
підрахунків і спробуємо ще раз. Надрукується:
20, 18, 15, 15, 12, 10, 9, 6, 5, 3.
Ми бачимо, що число 15 з’явилося двічі, і це не дивно, бо воно задовольняє
обом вимогам – ділиться і на 3, i на 5. Так само трапиться із будь-яким іншим
числом, що ділиться на 15.
Можливий вихід із скрути полягає в тому, щоб віднімати одиницю від лі-
чильника count всякий раз, коли зустрінеться число, що ділиться на 15, тоді воно
не буде враховано двічі. Остаточна програма виглядає так.
let n = 10;
let count = 0;
while (n > 0) {
if (n % 3 == 0)
count = count + 1;
if (n % 5 == 0)
count = count + 1;
if (n % 15 == 0)
count = count - 1;
n = n - 1;
}
console.log(count);
В програмуванні кожна задача має багато рішень, і перше знайдене рі-
шення не завжди буде найкращим. Ось ще один варіант, він коротший і до того
ж буде працювати швидше.
let n = 20;
let count = 0;

11
while (n > 0) {
if (n % 3 == 0)
count = count + 1;
else if (n % 5 == 0)
count = count + 1;
n = n - 1;
}
console.log(count);

1.3 Приклади аудиторних і домашніх задач

1. Дано три числа, a, b і с. Помістити в змінну max найбільше з них.


2. Охарактеризувати задане число x як від’ємне, нуль або додатне, тобто, в
залежності від значення змінної x присвоїти змінній char одне з трьох слів:
"від’ємне", "нуль", "додатне"
3. Написати програму, в якій ввести значення n та знайти суму виразу
1 + 1/2 + …+ 1/n.
4. Написати програму, в якій ввести значення n та знайти суму виразу
1*1 + 3*3 + 5*5 + … + (2n – 1)*(2n – 1).
Числа можна ввести за допомогою браузерної функції promp().
5. Написати програму, в якій ввести значення n та знайти суму ступенів
двійки з 0-ї по n-ю: 2^0 + 2^1 + 2^2 +…+ 2^n.
6. Вводити числа, поки не буде введено від’ємне число та підрахувати до-
буток додатних чисел.
7. Задано n, обчислити n!

12
2 ФУНКЦІЇ

2.1 Мета заняття

Навчитися оформлювати програмний код у вигляді окремих функцій.

2.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
08 Функції;
09 Граматика функцій;
10 Параметри і аргументи.

Щоб користуватися функціями, треба знати декілька формальних правил.


Розгляд прикладів почнемо з задачі, яку ми вирішували на першому занятті.
Задача «3 або 5 (функція)»
Підрахувати, скільки натуральних чисел, які не перевищують заданого чи-
сла n, діляться без остачі на 3 або на 5. Для підрахунку визначити функцію, яка
отримує n і повертає кількість чисел.
Вирішення задачі
По-перше, треба дати функції адекватне ім’я. Це дуже важливо, коли фун-
кція знаходиться у складі програми або бібліотеки. В нашому випадку це не так,
але тренуватися необхідно. Ім’я повинно відповідати призначенню функції і за-
звичай складається з дієслова та іменника, наприклад, «підрахуватиЧисла», але
англійською – countNumbers.
По-друге, з’ясувати, що буде параметрами функції. Виходячи з поперед-
нього рішення
let n = 20;
let count = 0;
while (n > 0) {

13
if (n % 3 == 0)
count = count + 1;
else if (n % 5 == 0)
count = count + 1;
n = n - 1;
}
console.log(count);
параметром треба зробити змінну n, яка саме і робить нашу програму придатною
для вирішення нескінченної кількості схожих задач.
Тепер мі готові написати заголовок функції.
function countNumbers(n)
У якості тіла функції буде код попереднього рішення за винятком остан-
ньої команди, бо наша функція повинна не друкувати, а повертати кількість чи-
сел.
function countNumbers(n)
{
let count = 0;
while (n > 0) {
if (n % 3 == 0)
count = count + 1;
else if (n % 5 == 0)
count = count + 1;
n = n - 1;
}
return count;
}
Перевірити, як працює функція, можна в консолі.
> console.log(countNumbers(20))

14
Задача «Сума двох менших»
Визначити функцію, яка отримує три числа і повертає суму двох наймен-
ших з них.
Вирішення задачі
Функції можуть мати будь-яку кількість параметрів, в даному випадку їх
буде три.
function smollestSum(a, b, c)
Суму двох із трьох чисел можна створити трьома способами:
let s1 = a + b;
let s2 = a + c;
let s3 = b + c;
Можна зрозуміти, що коли два числа менші за третє, то їх сума буде най-
меншою з усіх можливих. Обрати найменше з декількох чисел здатна стандартна
функція на ім’я Math.min.
В цілому функція може виглядати так.
function smollestSum(a, b, c)
{
let s1 = a + b;
let s2 = a + c;
let s3 = b + c;
return Math.min(s1, s2, s3);
}
Команда return змушує функцію обчислити вираз, розташований після
слова «return», і повернути його значення.
Якщо всі числа скласти, а потім відняти найбільше з них, то тут також
вийде сума двох менших. Стандартна функція Math.max допоможе знайти най-
більше з усіх чисел.
Такі міркування призводять до іншого вирішення тієї ж самої задачі.
function smollestSum(a, b, c)
{
return a + b + c - Math.max(a, b, c);

15
}

2.3 Приклади аудиторних і домашніх задач

1. Визначить функцію avg, яка отримує два числа і повертає їх середнє ари-
фметичне.
2. Визначить функцію, яка отримує ціле додатне число n і повертає знако-
змінну суму 1 – 2 + 3 – 4 + 5 – 6 +… - n.
3. Визначить функцію f(n), яка повертає суму непарних членів ряду Фібо-
наччі, які за розміром не перевищують числа n.
Наприклад, f(4) = 1 + 1 + 3 = 5 (число 2 парне, тож до суми не увійшло).
4.Визначить функцію, яка на вхід отримує ціле число і повертає кількість
цифр у ньому.
5. Визначить функцію, яка на вхід отримує ціле число і повертає суму не-
парних за значенням цифр цього числа.

16
3 СТОРІНКИ HTML

3.1 Мета заняття

Мета заняття – навчитися надавати програмам користувацький інтерфейс


за допомогою html-сторінок.

3.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб уважно прочитати те, що напи-


сано нижче.
Структура сторінки
JS не має власних засобів спілкування з користувачем, їх забезпечує сере-
довище виконання.
Веб-браузер надає веб-сторінки та мову розмітки HTML. Сторінка має на-
ступну структуру
<!DOCTYPE HTML>
<html>
<head>
<!-- тут інформація для браузера -->
</head>
<body>
<!-- тут інформація для користувача -->
</body>
</html>
Теги елементів
<div> - розділ документа.
<h1>, <h2>,... - заголовки різних рівнів.
<textarea> - багаторядкове поле введення.
<button> - кнопка.
<input> - різні елементи керування.

17
<script> - містить код JS, або посилання на окремий файл з кодом.
Приклад сторінки
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Приклади тегів</h1>
<div>
Це звичайний текст, его можна тільки читати.
</div>
<textarea>
Цей текст можна читати
і редагувати.
</textarea>
<div>
<button>Готово</button>
</div>
<script>
// А тут незабаром з’явиться код на javascript.
</script>
</body>
</html>

Браузер зберігає документ як ієрархічну систему елементів. Один тег –


один елемент. Вид тегу визначає тип елементу.
Кореневий об'єкт завантаженого документа називається document.
Атрибути
Атрибути тегів задають властивості елемента.
id - унікальний ідентифікатор елемента.

18
type - різновид елемента input:
- type="text" – однорядкове поле для введення тексту;
- type="button" - кнопка, яку можна натискати мишею;
- type="checkbox" – перемикач;
value - значення елемента input.
Доступ до елементів із JS
На будь-який елемент документа можна отримати посилання за допомогою
одного з методів об’єкта document. Найчастіше це метод getElementById, який
знаходить елемент по його id.
<textarea id="area">Який-небудь текст</textarea>
<script>
const area = document.getElementById("area");
alert(area.value)
</script>
Події
Коли користувач працює з документом, його активність спричиняє різно-
манітні події, такі як:
keydown - натиснута клавіша,
keyup - клавіша відпущена,
mousemove - перемістився курсор миші,
mousedown - натиснута кнопка миші,
mouseup - відпущена кнопка миші,
click - клацнули мишею.
Події відбуваються на елементах, з якими працює користувач. Будь-яка по-
дія будь-якого елемента може бути пов'язана з функцію, що буде автоматично
викликатися при настанні події. Таку функцію називають слухачем події.
Припустимо, є кнопка
<button id='ready'>Ready</button>
і функція
<script>
function f() {

19
alert('Кнопку "Ready" хтось натиснув.');
}
</script>

Зробим функцію f слухачем події 'click' кнопки ready. Для цього:


1) отримаємо посилання на кнопку
let ready = document.getElementById('ready');
2) добавимо слухача події click
ready.addEventListener('click', f);

Задача «Інтерактивний НОД»


Надати можливість користувачу знаходити НОД двох довільних чисел за
наступним сценарієм.
1. Користувач вводить два числа, кожне в окреме поле введення,
і натискає кнопку з написом «Знайти НОД».
2. Відповідь з'являється в елементі <div> нижче кнопки.
Вирішення задачі
По-перше, запропонуємо сторінку з відповідним користувацьким інтер-
фейсом.
<!DOCTYPE HTML>
<html>
<head> </head>
<body>
<h1>Пошук НОД</h1>
<div>
Перше число <input type="text" id="first" />
</div>
<div>
Друге число <input type="text" id="second" />
</div>
<div>

20
<input type="button" id="findButton"
value="Знайти НОД" />
</div>
<div id="result"></div>
<script src="gcd.js"></script>
</body>
</html>

По-друге, у окремому файлі розташуємо функцію, яка знаходить НОД.


function gcd(a, b) {
if (!b)
return a;
return gcd(b, a % b);
}
По-третє, в тому ж файлі визначим функцію, яка має виконуватися, коли
натискають на кнопку, і зробим її слухачем кнопкової події click.
// посилання на потрібні нам елементи сторінки
let first = document.getElementById('first');
let second = document.getElementById('second');
let result = document.getElementById('result');
let button = document.getElementById('findButton');

// функція, що буде виконуватися при натисканні кнопки


function buttonClick() {
let a = +first.value;
let b = +second.value;
result.innerHTML = gcd(a, b);
}

// додавання слухача події


findButton.addEventListener('click', buttonClick);

21
Зауважимо, що завдяки деяким рисам JS, увесь код третього пункту можна
скороти до наступного.
findButton.addEventListener('click', function() {
result.innerHTML = gcd(first.value, second.value);
});

3.3 Приклади аудиторних і домашніх задач

1. Створити сторінку для переводу температури зі шкали Фаренгейта в


шкалу Цельсія і навпаки.
2. Створити сторінку для переводу суми грошей з доларів у гривні і навпаки
по заданим курсам покупки і продажу доларів.
3. Створити сторінку для гри у хрестики-нулики на полі розміром 3x3.
4. Створити сторінку для гри у шашки двох гравців, які будуть ходити по
черзі.

4 МАСИВИ

4.1 Мета заняття

Почати нарешті використовувати масиви у програмах.

4.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
11 Масиви;
12 Цикл for;
13 Методи масивів.

22
Масиви у JS - це базовий спосіб оперувати з великою кількістю даних в
пам’яті.
Задача «Створити і заповнити масив»
Створити масив із n елементів і чимось його заповнити, наприклад, нулями.
Вирішення задачі
За допомогою літералу можна створити лише малий масив і лише з кількі-
стю елементів, яка відома під час написання програми.
let a = [1, 2, 3];
Якщо кількість елементів встановлюється під час виконання програми, тут
допоможе лише конструктор масиву.
let a = new Array(n);
Звісно, аргумент конструктору має бути визначений на час виклику конс-
труктора.
Заповнити масив нулями можна за допомогою циклу.
for (let i = 0; i < a.length; a++) {
a[i] = 0;
}
або скористуватися методом масиву fill.
a.fill(0);
Створення і первісне заповнення масиву можна поєднати.
let a = new Array(n).fill(0);
Задача «Знайти найбільше число»
Заданий числовий масив. Знайти найбільше число в масиві і місце його зна-
ходження. Якщо таких чисел декілька, то знайти місце знаходження першого з
них.
Вирішення задачі
Під «місцем знаходження» розуміється індекс в масиві. Якщо відомий ін-
декс, то число також буде відомо, тому будемо шукати саме індекс, а не число.
Для перевірки майбутньої програми приготуємо невеликий масив.
let a = [4,3,5,2,5];

23
Індекс найбільшого числа в нашій програмі буде зберігати змінна imax.
Спочатку припустимо, що перше число в масиви (його індекс 0) і є найбільше.
let imax = 0;
Далі пройдемо увесь масив до кінця, і для кожного елемента масиву будемо
перевіряти, чи не перевищує він той, що вважається найбільшим саме зараз.
Якщо перевищує, будемо виправляти imax.
for (let i = imax + 1; i < a.length; i++)
if (a[i] > a[imax])
imax = i;
Коли цикл скінчиться, в змінній imax залишиться індекс першого з найбі-
льших чисел масиву.
Зберемо все разом і добавимо друк для перевірки.
let a = [4,3,5,2,5]

let imax = 0;
for (let i = imax + 1; i < a.length; i++)
if (a[i] > a[imax])
imax = i;

console.log(imax, a[imax])
Наша програма заслуговує на те, щоб стати окремою функцією. Парамет-
ром функції буде числовий масив, а повертати вона буде місце знаходження пер-
шого з найбільших чисел масиву.
function getIndexMax(a)
{
let imax = 0;
for (let i = imax + 1; i < a.length; i++)
if (a[i] > a[imax])
imax = i;
return imax;
}

24
Задача «Вставка в упорядкований масив»
Існує числовий масив, елементи якого впорядковані за зростанням. Також
є число, яке необхідно додати до масиву, причому так, щоб порядок не поруши-
вся.
Вирішення задачі
Можна просто додати число до масиву методом push, а потім відсортувати
методом sort. Але такий спосіб, хоча легкий для програміста, важкий для
комп’ютера, він потребує від нього багато зайвої роботи. І хоча комп’ютери для
того і створені, щоб працювати замість нас, будемо діяти в припущенні, що сор-
тування не існує (до речі, сортування можна визначити саме через вставку, яку
ми збираємося запрограмувати).
Алгоритм буде такий.
1. Додаємо до масиву ще один елемент, причому неважливо, який.
2. Встановлюємо покажчик на передостанній елемент масиву.
3. Якщо елемент під покажчиком менший за додаткове число, ставимо до-
даткове число одразу за покажчиком і зупиняємось.
4. Копіюємо елемент під покажчиком у комірку справа від покажчика, зме-
ншуємо покажчик на одиницю і повторюємо пункти 3 і 4.
Під покажчиком слід розуміти індекс у масиві, тобто ціле число.
Реалізуємо алгоритм у вигляді функції insertIntoOrd, яка приймає масив a і
число x. Повертати така функція нічого не зобов’язана (і тому поверне undefined).
function insertInOrd(a, x)
{
a.push(x); // пункт 1
let p = a.length – 2; // пункт 2
while (p >= 0) {
if (a[p] < x) {
a[p + 1] = x; // пункт 3
return;
}
a[p + 1] = a[p]; // пункт 4

25
p--;
}
}
Для перевірки функції напишемо програму з трьох рядків.
let a = [2,3,5,7,9]
insertInOrd(a, 6);
console.log(a);
Вона надрукує [2, 3, 5, 6, 7, 9], що втішає. Але, як відомо, тестування до-
водить не відсутність помилок, а лише їх наявність, тому продовжимо переві-
ряти.
insertInOrd(a, 1);
Надруковано [2, 2, 3, 5, 7, 9], а має бути [1, 2, 3, 5, 7, 9]. Тобто, коли додат-
кове число менше початкового елементу масиву, щось іде не так.
Ну дійсно, в цьому разі цикл закінчується за невиконанням умови циклу, і
тоді ніякого запису числа в масив не відбувається. Виправимо це, додавши ко-
манду
a[0] = x;
одразу після циклу.
Після всякої зміни в програмі зазвичай повторюють усі тести. У нас їх було
два – перший перевіряв загальний випадок, другий – один з крайніх випадків.
Було б доцільно добавити інший крайній випадок, коли додаткове число більше
за найбільший елемент масиву, і тести на співпадіння додаткового числа з сере-
дніми та крайніми елементами.
Сподіваємось, ви це зробите самостійно.

4.3 Приклади аудиторних і домашніх задач

1. Створіть масив з n елементів і запишіть в нього n перших членів ряду


Фібоначчі. Наприклад, для n = 7 це буде [1, 1, 2, 3, 5, 8, 13].
2. Створіть двовимірний масив arr розміром n на m елементів і запишіть в
нього нулі и одиниці в шаховому порядку. В комірці arr[0][0] нехай буде 0.
26
3. Є числовий масив. Замінити у ньому всі входження числа a на число b.
Для цього визначте функцію replace(arr, a, b), яка нічого не повертає, але змінює
масив.
> let arr = [1,2,3,1,2,3];
> replace(arr, 3, 4);
> console.log(arr);
[1,2,4,1,2,4];
4. Визначте функцію exclude(), яка отримує два масиви і видаляє з першого
масиву всі елементи, що зустрічаються у другому масиві.
Відомо, що в масивах немає елементів, які повторюються.
> console.log(exclude(['a','b','c','d'], ['b','d','e','f']));
['a','c' ].
5. Існує дворівневий масив масивів із цілих чисел, тобто, масив, елементи
якого є масивами. Виробити з дворівневого масиву новий однорівневий масив.
Визначте функцію flatten(arr), яка отримує дворівневий масив та повертає
однорівневий.
> console.log( flatten( [[1,2,3],[4,5],[1,2]]));
[1,2,3,4,5,1,2].

5 СЕРЕДОВИЩЕ РОЗРОБКИ

5.1 Мета заняття

Почати використовувати середовище розробки для створення програм.

5.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб уважно прочитати те, що напи-


сано нижче.

27
Інтегроване середовище
Програми на JS можна писати, налагоджувати і виконувати за допомогою
веб-браузера, але це не буде діями професійного програміста. Значно вигідніше
користуватися для розробки інтегрованим середовищем, яке містить в собі реда-
ктор з підсвічуванням коду, компілятор, збирач, відладчик і ще багато корисного.
Платформа Node.js
Перший крок до мети заняття – встановити на комп’ютері програму
Node.js. Node.js® — це JavaScript-оточення, побудоване на JavaScript-рушієві
Chrome V8. Ця програма дозволяє розробляти і виконувати JS-програми незале-
жною від веб-браузерів, які, до речі, призначені зовсім для іншого.
Щоб встановити Node.js, треба перейти на сайт розробника
https://nodejs.org/uk і виконати покрокову інструкцію.
Редактор Visual Studio Code
Найбільш поширеними середовищами розробки на JS є програми
WebStorm компанії JetBrains і Visual Studio Code компанії Microsoft. Кожна з про-
грам може виконуватися на будь-якій з відомих платформ: Windows, Linux,
macOS. WebStorm є комерційним продуктом (хоча для навчальних цілей існує
безкоштовна ліцензія), VS Code є цілком безкоштовною, і тут ми розглянемо
саме цю програму.
Встановити VS Code так же просто, як і Node.js – переходьте на сайт
https://code.visualstudio.com/ і виконуйте інструкцію.
Розширення VS Code
Точніше кажучи, VS Code це не інтегроване середовище, а редактор про-
грамного коду. Але завдяки своїм розширенням, він легко перетворюється в се-
редовище розробки програм будь-якою мовою програмування. Виключенням є
мова JavaScript, яка є внутрішньою мовою редактору, і редактор має вбудовані
підкажчик, налагоджувач, здатен форматувати код, здійснювати навігацію по
коду, рефакторинг, тобто є повноцінним середовищем розробки, навіть без роз-
ширень.

28
Тим не менш, для програмування на JS вельми корисним буде розширення
ESLint, яке допоможе покращити ваш код, тому знайдіть його серед рекомендо-
ваних (панель Extensions) і встановіть. Після встановлення закрийте и знову від-
крийте VS Code.
Цикл розробки в VS Code
Сучасна розробка програм є ітеративною, вона полягає в поступовому вне-
сенню невеликих змін в код програми. Після кожної зміни код компілюється і
програміст повідомляється про знайдені компілятором помилки. Якщо помилок
не знайдено, скомпільований код виконується і показується результат вико-
нання. Далі цикл розробки повторюється.
У VS Code це виглядає так.
1. Спочатку відкривають порожню папку (меню File/Open Folder) і ство-
рюють новий файл (меню File / New File).
2. Одразу зберігають файл з розширенням .js (меню File / Save As).
3. Заносять в файл якийсь код, наприклад,
console.log("Hello");
4. Дають команду на компіляцію і виконання (меню Run / Run Without
Debugging).
Результат виконання буде видний на панелі DEBUG CONSOLE.
Для налагодження програми із зупинками на контрольних точках запус-
кайте програму командою Run / Start Debugging.
Докладніше про можливості VS Code можна прочитати тут [2].
Програми з графічним інтерфейсом
Такі програми виконуються в браузері, але розробляють їх в інтегрованому
середовищі, зокрема, в VS Code. Обов’язково має бути html-файл із веб-сторін-
кою. Код JS бажано зберігати в окремому файлі, якщо програма не маленька,
таких файлів може бути декілька.
Як приклад розглянемо програму «Інтерактивний НОД» із практичного за-
няття «Сторінки HTML».
Зайдемо у VS Code і відкриємо пусту папку. Код веб-сторінки помістимо у
файл index.html, а код програми помістимо у файл gcd.js.

29
Щоб запустити програму на виконання, треба зробити активною вкладку із
веб-сторінкою і вибрати пункт меню Run / Run Without Debugging. Ефект буде
рівно такий, якби ви клацнули мишею по файлу index.html у провіднику.
Відлагодження програми також можливе – меню Run / Start Debugging. Код
JS буде зупинятися в контрольних точках, якщо ви їх поставите.

5.3 Приклади аудиторних і домашніх задач


1. Визначте функцію sumGarm(n), яка отримує число n та повертає суму
1 + 1/2 + 1/3+...+1/n.
2. Визначте функцію word(n), яка отримує однорозрядне число n
(0 <= n < 10) і повертає його прописом українською мовою.
3. Запрограмуйте калькулятор із двома полями для вводу чисел і кнопками
для операцій +, -, *, /, %, **.
4. Запрограмуйте гру Джона Конвея «Життя» [3].

6 РЯДКИ

6.1 Мета заняття

Навчитися працювати з текстом програмними засобами.

6.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
14 Рядки;
15 Методи рядків.

Рядки схожі на масиви символів, які не можна змінювати. Не дивно, що


вони мають деякі методи масиву з тих, що не змінюють свого власника (indexOf,

30
slice, length) і жодного з тих, що змінюють. Втім, більшість методів специфічна
саме для текстових даних.
Задача «Анаграма чи ні»
Є два слова, треба з’ясувати, чи є друге слово результатом перестановки
літер першого слова, тобто анаграмою.
Наприклад, «задача» і «аздача» - анаграми, а «задача» і «задчад» - ні.
Вирішення задачі
Навіть з наведених прикладів видно, що недостатньо, щоб обидва слова
були однакової довжини і складалися з однакових букв. Треба щоб кожна буква
кожного слова в однаковій кількості зустрічалася в обох словах.
Звісно, можна безпосередньо зайнятися підрахунками, але простіше буде
впорядкувати букви в кожнім слові за алфавітом, а потім просто порівняти те,
що вийшло. Якщо буде співпадіння, но первісні слова – анаграми, якщо не буде,
то ні.
Перешкода в тому, що рядок не можна впорядковувати, але це долається
«перетворенням» рядка в масив методом split().
> 'задача'.split('')
[ 'з', 'а', 'д', 'а', 'ч', 'а' ]
Тепер можна впорядковувати.
> 'задача'.split('').sort()
[ 'а', 'а', 'а', 'д', 'з', 'ч' ]
Вочевидь, якщо початі зі слова «аздача», то все скінчиться таким же маси-
вом. Порівняємо їх.
> ['а','а','а','д','з','ч'] == ['а','а','а','д','з','ч']
false
Дивно, але факт, що JS вважає масив рівним тільки самому собі. В нас же
два різних масиви, хоч вони і виглядають однаково. Вихід в тому, щоб перед
порівнянням зробити з масиву знову рядок.
> 'задача'.split('').sort().join('')
'ааадзч'

31
Вже можна писати програму. Зробимо це у вигляді функції, яка отримує
два рядки і повертає логічне значення.
function isAnagram(a, b) {
return a.split('').sort().join('') ==
b.split('').sort().join('');
}

Перевірка.
> console.log(isAnagram('задача', "аздача"));
true
> console.log(isAnagram('задача', "задчад"));
False
Задача «Випадкова Анаграма»
Розробити функцію, яка отримує слово и виробляє його анаграму шляхом
випадкової перестановки букв слова.
Вирішення задачі
План такий – перетворити рядок в масив, якось перемішати елементи ма-
сиву, потім перетворити масив в рядок. Перший і третій кроки мі вміємо робити,
з’ясуємо, як вчиняти другий.
Один з можливих способів в тому, щоб випадково обрати номер одного
елементу, потім так само обрати номер іншого елементу, потім обміняти їх міс-
цями. Якщо повторити це достатню кількість разів, масив буде не впізнати.
Випадковість може внести в нашу програму стандартна функція
Math.random(). Вона повертає випадкове число в інтервалі від 0 до 1 виключно.
> Math.random()
0.5598596576013208
> Math.random()
0.8452219648347699
Щоб отримати ціле число в інтервалі від 0 до n виключно, треба зробити
деякі обчислення. Доручимо цю роботу окремій функції rnd().
function rnd(n) {

32
return Math.floor(Math.random() * n);
}
Основна функція нашого рішення буде такою.
function randomAnagram(word)
{
// перший крок – отримуємо масив
let arr = word.split('');
let n = arr.length;
// другий крок - робимо n обмінів
for (let k = 0; k < n; k++) {
let i = rnd(n);
let j = rnd(n);
let t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}
// третій крок – отримуємо рядок
return arr.join('');
}
Звісно, є шанс отримати на виході те ж саме слово, особливо, якщо вхідне
слово коротке або в ньому багато букв повторюються. Такого можна запобігти,
якщо замінити третій крок на таке.
// третій крок
let anag = arr.join('');
return anag != word ? anag : randomAnagram(word);

6.3 Приклади аудиторних і домашніх задач

1. Визначте функцію count, яка отримує рядок та символ (рядок довжини


1). Функція повинна визначити скільки разів заданий символ зустрічається в за-
даному рядку.
2. Визначте функцію palindrome, яка отримує рядок і повертає 1, якщо ря-
док є паліндромом, і 0 в іншому випадку.
33
Паліндромом зветься рядок, який однаково читається в обох напрямках,
наприклад, «топот», «потоп», «нажалкабаннабаклажан».
3. Визначте функцію subCount, яка отримує рядок та підрядок і підраховує
кількість входжень підрядка у рядок.
Домовимось, рахувати тільки такі входження, що не перетинаються. Тобто,
підрядок 'aaa' входить до рядка 'aaaaaa' два рази, а не чотири.
4. Визначте функцію rhyme, яка отримує два слова, наприклад, «комар» та
«кошмар». Якщо введені слова римуються, функція повертає 1, якщо не риму-
ються, повертає 0.
Не заглиблюючись у віршування, домовимось, що слова римуються, якщо
в них збігаються останні три літери, або якщо одне зі слів є закінченням іншого.
5. Визначте функцію beforeBirth(s1, s2), яка повертає кількість днів, що за-
лишилися до дня народження.
Функція отримує два рядки: в першому записана дата народження у фор-
маті: "дд.мм", а в другому – поточна дата у форматі: "дд.мм.рррр".
> console.log(beforeBirth("02.07","11.06.2016"));
21

7 ПОСИЛАЛЬНІ ТИПИ

7.1 Мета заняття

Усвідомити різницю між значущими і посилальними типами та особливо-


сті поведінки посилальних параметрів функцій.

7.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
17 Типи даних;
18 Посилальні типи .

34
Відмінності у використанні пам’яті
Як відомо, дані посилальних типів зберігаються в пам’яті розділено, що і
відрізняє їх від типів значущих. Це треба враховувати при присвоюванні і при
передаванні аргументів функціям. Останнє виконується за тими ж правилами, що
і присвоювання.
Розглянемо простий приклад, функція f отримує число і збільшує його на
одиницю.
function f(x) {
x += 1;
}
Виклик такої функції ніяк не вплине на її аргумент.
let a = 0;
f(a);
console.log(a); // надрукує 0
Інша справа, коли параметр функції посилального типу, скажімо, масив.
function g(x) {
x[0] += 1;
}
Елемент масиву, який функція змінювала, дійсно зміниться.
y = [0];
g(y);
console.log(y); // надрукує [1]
Різниця пояснюється тим, що в першому випадку копіювалося число, і фу-
нкція змінювала не саме число, а його копію.
В другому випадку копіювалося посилання на масив, тобто, його адреса в
пам’яті. Але копія адреси вказує на те ж саме місце в пам’яті, що і оригінал ад-
реси, тому зміна елементу масиву вдалася.
Задача «Доповнення масиву»
Визначте функцію, яка отримує масив і ще декілька елементів і доповнює
масив цими елементами.

35
Вирішення задачі
Назвемо функцію add, бо вона, дійсно, повинна щось додавати. Перший
параметр функції – масив, усі інші параметри – елементи, що додаються, їх кіль-
кість не обмежена.
Сучасний спосіб визначити функцію з довільною кількістю параметрів на-
дає операція розширення (spread).
function add(a, ...b)
Операція (...) призводить до того, що параметр b в тілі функції виглядає як
масив. При визові ж функції цей параметр буде отримувати своє значення від
довільної кількості аргументів.
add(a, 1, 2, 3);
Відомо, що два масиви можна поєднати за допомогою метода concat.
a.concat(b);
Спробуємо написати функцію в цілому
function add(a, ...b)
{
a = a.concat(b);
}
і випробувати її.
a = [1, 2];
add(a, 3, 4, 5);
console.log(a);
Спроба не вдалася, було надруковано
[1, 2]
тобто масив a залишився без змін.
Причина в тому, що метод concat не змінює свого володаря, а створює но-
вий масив, і коли ми здійснили присвоювання
a = a.concat(b);
то занесли адресу новоствореного масиву в змінну a. Адреса первісного масиву,
яка до того знаходилася в змінній a, була просто втрачена.

36
Очевидно, нам треба не створювати новий масив, а доповнювати той, що
вже існує. Додати елемент до масиву здатний метод push. Оскільки таких елеме-
нтів не один, а цілий масив, застосуємо цикл.
function add(a, ...b)
{
for (let i = 0; i < b.length; i++)
a.push(b[i]);
}
Після випробовування маємо очікуваний результат – [1, 2, 3, 4, 5].
На остачу зауважимо, що метод push визначений подібно до нашої функції,
тобто він здатен приймати довільну кількість аргументів. З другого боку опера-
ція розширення спрацьовує не тільки у визначенні, а і у визові функції, і це може
суттєво скоротити наш код.
function add(a, ...b)
{
a.push(...b)
}

7.3 Приклади аудиторних і домашніх задач

1. Визначте функцію myJoin(масив, роздільник), яка отримує масив та ря-


док-роздільник і повертає рядок, в який з'єднані всі елементи масиву із розділь-
никами між сусідніми елементами. З порожнього масиву має виходити порожній
рядок.
Не використовуйте метод join.
console.log(myJoin (["aa","bb","cc"],"--"));
"aa--bb--cc"
2. Визначте функцію myReverse(масив), яка отримує довільний масив і ро-
зташовує його елементи у зворотному порядку. Функція нічого не повертає,
лише змінює свій аргумент.
Не використовуйте метод reverse.

37
3. Визначте функцію myShift(масив), яка зробить те саме, що метод масиву
shift.
Не використовуйте метод shift.
4. Визначте функцію myUnshift(масив, значення), яка зробить те саме, що
метод масиву unshift.
Не використовуйте метод unshift.

8 ОБ’ЄКТИ

8.1 Мета заняття

Надбати навички роботи з об’єктами – основою складених даних у будь-


якій мові програмування.

8.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
19 Об'єкти;
20 Робота з об’єктами.

Конструювання об'єктів
Об’єкти ідеально підходять для програмного втілення різноманітних сут-
ностей предметної області.
Наприклад, сутність Студент має такі властивості: ім’я, група, рейтинг ус-
пішності, хобі (ми усвідомлюємо, що в реалі все набагато складніше, але спро-
щуємо для короткості прикладів).
Студент Шевченко може бути представлений у програмі наступним
об’єктом.
let andriy = {
name: "Шевченко",
38
group: "ПЗПІ-22-10",
rating: 66,
hobby: "футбол" };

Якщо в програмі потрібно мати багато подібних об’єктів, визначають клас.


class Student {
constructor(name, group, rating, hobby) {
this.name = name;
this.group = group;
this.rating = rating;
this.hobby = hobby;
}
}
Клас відіграє роль фабрики об’єктів і дозволяє створювати їх за допомогою
спеціального синтаксису зі словом new. Ось один студент:
let andriy =
new Student("Шевченко", "ПЗПІ-22-10", 66, "футбол");
А ось масив із трьох студентів.
let students = [
new Student("Петренко", "ПЗПІ-21-10", 88, "шахи"),
new Student("Шельменко", "ПЗПІ-21-11", 77, "кулінарія"),
new Student("Карпенко", "ПЗПІ-21-12", 79, "спів") ];
Всі студенти мають однаковий набір властивостей, хоча значення тих вла-
стивостей різні.
Після того, як об’єкт створений, його властивостям можна надавати інші
значення.
andriy.hobby = "теніс";
andriy.hobby = "сноуборд";
Можна добавляти об’єкту нові властивості.
andriy.medal = "gold";
Надану таким чином властивість буде мати лише один об’єкт – andriy.

39
Задача «Морський бій»
Підготувати необхідні об’єкти для програмування гри «Морський бій»
Вирішення задачі
На думку спадають два різновиди об’єктів, море – Sea і корабель - Ship.
Об’єкт Sea уособлює простір, в якому розташовані кораблі. Це буде квад-
рат зі стороною в n одиниць. Однією з властивостей моря буде масив кораблів.
class Sea {
constructor(size) {
this.size = size;
this.ships = [];
}
}
Наразі у складі класу Sea є лише конструктор. Він отримує розмір моря і
зберігає у властивості size об’єкту, який створює. В коді класу цей об’єкт пред-
ставлений посиланням this.
Клас Ship уособлює корабель. Корабель має розмір (ціле від 1 до 4), орієн-
тацію (горизонтальна або ні) і координати (x та y). Все це отримує конструктор і
зберігає у властивостях об’єкту, який створює, тобто у властивостях корабля.
class Ship {
constructor(size, isHor, x, y) {
this.x = x;
this.y = y;
this.size = size;
this.isHor = isHor;
}
}
Наразі ми можемо створити море
const sea = new Sea(10);
і скільки завгодно кораблів,
const ship1 = new Ship(3, true, 0, 0);
const ship2 = new Ship(3, false, 2, 2);

40
// ... тут інші кораблі
але вони ніяк не будуть пов’язані між собою. Щоб зв’язати корабель ship1 і море
sea, треба:
1) додати корабель до масиву sea.ships;
sea.ships.push(ship1);
2) надати кораблю посилання на море.
ship1.sea = sea;
Ці речі треба робити сумісно, тому варто надати морю метод addShip, якій
зробить необхідне.
class Sea {
constructor(size) {
this.size = size;
this.ships = [];
}

addShip(ship) {
this.ships.push(ship);
ship.sea = this;
}
}
Тепер створення моря з кораблями виглядає так.
const sea = new Sea(10);

sea.addShip(new Ship(3, true, 0, 0));


sea.addShip(new Ship(3, false, 2, 2));
// ... тут інші кораблі
Класи Sea і Ship помістимо в файл models.js, код створення моря з кораб-
лями у файл main.js і на цьому можна вважати першу версію об’єктної моделі
закінченою.

41
Відображення моделі
Спробуємо відобразити нашу модель на веб-сторінці. Це можна зробити
різними способами, але нам доцільно обирати найпростіший.
Зупинимось на таблиці розміром n на n з квадратними комірками.
<!DOCTYPE HTML>
<html>
<head> </head>
<body>
<style>
td {
width: 20px;
height: 20px;
}
</style>
<h1>Sea Battle</h1>
<table id="sea"></table>
<script src="models.js"></script>
<script src="main.js"></script>
</body>
</html>
Неможливо заздалегідь створити таблицю розміром n на n, такі речі роб-
ляться програмно. Добавимо код створення таблиці в файл main.js.

let seaTable = document.getElementById("sea");

for (let y = 0; y < sea.size; y++ ) {


const tr = seaTable.insertRow();
for (let x = 0; x < sea.size; x++ ) {
const td = tr.insertCell();
}
}

42
Нарешті можна вдатися до відображення нашої моделі. Доручимо це фун-
кції drawModel.
function drawModel() {
// sea
for (let x = 0; x < sea.size; x++ )
for (let y = 0; y < sea.size; y++ )
seaTable.rows[y].cells[x].style.backgroundColor =
"lightBlue";
// ships
for (const ship of sea.ships )
drawShip(ship);
}
Окремий корабель відображує функція drawShip.
function drawShip(ship) {
if (ship.isHor) {
let y = ship.y;
for (let x = ship.x; x < ship.size + ship.x; x++)
seaTable.rows[y].cells[x].style.backgroundColor =
"red";
} else {
let x = ship.x;
for (let y = ship.y; y < ship.size + ship.y; y++)
seaTable.rows[y].cells[x].style.backgroundColor =
"red";
}
}
Функції drawModel і drawShip тимчасово помістимо до файлу main.js і за-
вершимо все командою виклику.
drawModel();

43
8.3 Приклади аудиторних і домашніх задач

1. Спроектуйте сукупність об’єктів для гри «Minesweeper» [4].


2. Спроектуйте сукупність об’єктів для комп’ютерної моделі Сонячної си-
стеми. Очікувані класи: Система, Планета, Супутник.
3. Спроектуйте сукупність об’єктів для університетської бібліотеки. Очіку-
вані класи: Книга, Читач, Формуляр.

9 РЕКУРСІЯ

9.1 Мета заняття

Придбати навички розробки та реалізації рекурсивних алгоритмів.

9.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
25 Рекурсія;
26 Задача про ханойські вежі.

Задача «Сума масиву»


Знайти суму елементів числового масиву і зробити це за допомогою реку-
рсії.
Вирішення задачі
Можна сказати, що сума елементів непустого масиву складається з пер-
шого елементу і суми масиву, складеного з усіх елементів, окрім першого.
sum([3,4,5]) = 3 + sum([4,5])
До цього слід додати, що сума елементів пустого масиву дорівнює нулю.
sum([]) = 0
Наслідком таких міркувань буде функція sum.
44
function sum(a) {
if (a.length == 0)
return 0;
return a[0] + sum(a.slice(1));
}
Зрозуміло, що немає практичного сенсу у застосування рекурсії для підра-
хунку суми, але, як вправа, задача корисна.
Задача «Усі перестановки»
Отримати всі можливі перестановки елементів заданого масиву. Напри-
клад, заданий масив [1,2,3]. З нього вийдуть такі перестановки.
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]
Визначимо функцію permutations, яка отримує масив і повертає масив переста-
новок.
> permutations([1,2,3])
[
[ 1, 2, 3 ],
[ 1, 3, 2 ],
[ 2, 1, 3 ],
[ 2, 3, 1 ],
[ 3, 1, 2 ],
[ 3, 2, 1 ]
]
Вирішення задачі
Поглянемо на приклад вище. Всі перестановки поділяються на сукупності,
які не перетинаються.
Перша сукупність складається з усіх перестановок масиву [2,3] (це [2,3] і
[3,2]) до яких додана 1 спереду. Масив [2,3] не випадковий, він утворився шля-
хом вилучання 1 з початкового масиву [1,2,3].
З цього маємо спосіб конструювання першої сукупності перестановок:
1) вилучити перший елемент з масиву,
2) створити всі перестановки з елементів, які залишилися,

45
3) долучити перший елемент до кожної створеної перестановки.
Так само вчинимо з усіма іншими елементами первісного масиву і отрима-
ємо всі інші сукупності.
На завершення поєднаємо сукупності в одному масиві, який і стане резуль-
татом роботи в цілому.

function permutations (arr)


{
let n = arr.length;
if (n == 0)
return [[]];
let result = [];
for (let k = 0; k < n; k++) {
let x = arr[k];
let xs = arr.filter((x, i) => i != k)
let ps = permutations(xs).map(a => [x].concat(a));
result.push(...ps);
}
return result;
}
Розглянемо деякі рядки наведеного коду окремо.
if (n == 0) return [[]];
Можливо, це виглядає дивним, але існує одна перестановка пустого масиву і це
– пустий масив.
for (let k = 0; k < n; k++) ...
По черзі створюємо всі сукупності перестановок.
let x = arr[k];
У змінній x – k-й елемент первісного масиву.
let xs = arr.filter((x, i) => i != k)
У змінній xs – первісній масив, в якому нема k-того елементу.
let ps = permutations(xs).map(a => [x].concat(a));

46
У змінній ps – усі перестановки масиву xs, доповнені спереду елементом x, тобто,
чергова сукупність перестановок.
result.push(...ps);
До кінцевого результату додається чергова сукупність перестановок.
Рекурсивні дані
Рекурсивними можуть бути не лише функції, а і дані. Найпростіший прик-
лад – зв’язаний список.
Розглянемо об’єкт з двома полями – load і next. В першому полі зберіга-
ються дані, це, так би мовити, корисне навантаження. Друге поле містить поси-
лання на схожий об’єкт, або null.
let e1 = {load: 100, next: e2};
let e2 = {load: 200, next: e3};
let e3 = {load: 300, next: null};
Те ж саме можна записати у вигляді єдиного виразу
let e1 = {load: 100, next:
{load: 200, next:
{load: 300, next: null}}};
який краще показує, що перед нами не три окремих елементи, а зв’язаний список.
З виразу видно, що список має рекурсивну природу. База рекурсії – пустий
список, він позначається константою null.
Рекурсивні дані природно обробляти рекурсивними алгоритмами.
Функція sumList складає все, що знаходиться у списку.
function sumList(lst) {
return lst ? lst.load + sumList(lst.next) : 0;
}
Функція printList друкує список поелементно. Звісно, друкувати посилання
не потрібно, лише корисне навантаження.
function printList(lst) {
if (lst) {
console.log(lst.load);
printList(lst.next);

47
}
}
Функція lengthList рахує елементи списку, тобто вимірює його довжину.
function lengthList(lst) {
return lst ? 1 + lengthList(lst.next) : 0;
}

9.3 Приклади аудиторних і домашніх задач

1. У вирішенні задачі «Сума масиву» масив зменшувався на один елемент


за один рекурсивний виклик. Як наслідок, глибина рекурсії дорівнювала розміру
масиву.
Переробить алгоритм так, щоб сума масиву обчислювалася як сума двох
половин масиву. Це суттєво зменшить глибину рекурсії (спробуйте сказати, як
саме).
2. Визначте функцію rmax(arr), яка отримує непустий масив arr та повертає
найбільший із його елементів.
3. Задано слово. Виробіть з нього усі можливі анаграми. Скористайтеся рі-
шенням задачі «Усі перестановки», але зауважте, що коли букви в слові повто-
рюються, серед перестановок будуть однакові. Повтори в остаточне рішення не
включайте.
4. Свого часу Ісаак Ньютон обчислював квадратні корени шляхом послідо-
вних наближень. Перше наближення приймалося як половина того числа x, з
якого витягувався корінь y, тобто, y = x/2.
Всі наступні наближення знаходились за формулою y = (y + x/y) / 2.
Визначте рекурсивну функцію, яка обчислить квадратний корінь методом
Ньютона з точністю < 0.001% від заданого числа x.
5. Визначте функцію append, яка отримує два зв'язні списки і додає другий
список до першого. Функція повертає undefined, але змінює перший список.

48
6. Визначте рекурсивну функцію reverse, яка отримує зв'язаний список і
створює інший список, в якому корисне навантаження розташовано у зворот-
ному порядку
> reverse( {load:10, next:{ load:20, next:null}} )
{ load:20, next:{ load:10, next:null}}

10 ФУНКЦІОНАЛЬНЕ ПРОГРАМУВАННЯ

10.1 Мета заняття

Ознайомитися з функціональним стилем програмування шляхом вирі-


шення декількох типових задач.

10.2 Методичні вказівки з організації самостійної роботи студентів

Підготовка до заняття полягає в тому, щоб відновити в пам’яті зміст лекцій,


перечитавши наступні розділи посібника [1]:
40 Функціональне програмування;
41 ФП. Списки та кортежі.

Задача «Видалити входження»


Визначте функцію del, яка отримує елемент і список і видаляє всі вхо-
дження елемента у список.
Вирішення задачі
Не будемо сприймати слова про видалення буквально, в Гаскелі не мож-
ливо нічого змінити, там все константи. Йдеться про те, що буде створений но-
вий список, очищений від входжень того, що видаляється.
Почнемо з рекурсивного рішення. Базу рекурсії становить пустий список, і
тут навіть не важливо, що саме видаляється.
del _ [] = []
Коли список не пустий, треба врахувати два випадки.
49
Перший випадок – початковий елемент списку співпадає з таким, що вида-
ляється. Тоді цього елементу вже не буде в новому списку, а долю хвоста списку
має вирішити та ж сама функція del.
У другому випадку – перший елемент списку переходить в новий список, а
хвостом знову опікується функція del.
del y (x : xs)
| x == y = del y xs
| otherwise = x : del y xs

Інший спосіб вирішення тієї ж задачі дасть генератор списку.


del y xs = [x | x <- xs, x /= y ]

Ще коротше рішення надає функція filter. Тут можна використати безточ-


ковій стиль запису.
del y = filter (/= y)
Задача «Сума цифр»
Визначте функцію sumDigs, яка отримує ціле число і повертає суму його
цифр.
Вирішення задачі
Візьмемо якесь число, наприклад, 245. Молодша цифра цілого числа дорі-
внює остачі від ділення цього числа на 10.
> 245 `mod` 10
5
Частка від ділення на 10 становить інше число – 24
> 245 `div` 10
24
цифри якого співпадають з цифрами первісного числа, за виключенням той, що
ми знайшли за допомогою функції mod. Тобто, сума цифр числа 245 дорівнює
(сумі цифр числа 24) + 5.
Це дає нам спосіб знаходження суми цифр довільного числа n.
sumDigs n = sumDigs (n `div` 10) + n `mod` 10

50
Якщо продовжити обчислення, то вийде наступне:
> 24 `mod` 10
4
> 24 `div` 10
2
> 2 `mod` 10
2
> 2 `div` 10
0
і наприкінці нам треба буде знаходити суму цифр числа 0 (до речі, так станеться
незалежно від того, з якого числа мі починали, це математична необхідність).
Легко зрозуміти, що така сума дорівнює нулю.
sumDigs 0 = 0
Два рівняння разом становлять визначення функції sumDigs.
sumDigs 0 = 0
sumDigs n = sumDigs (n `div` 10) + n `mod` 10
Послідовність рівнянь має бути саме такою, інакше нам загрожує нескін-
ченне обчислення.
Інший спосіб вирішення полягає в тому, щоб перетворити число в рядок,
переробити кожен символ рядка на число и ті числа скласти.
245 => "245" = ['2', '4', '5'] => [2, 4, 5] => 11
Це можна зробити так.
sumDigs' n = sum [read [c] | c <- show n]
Задача «Швидке сортування»
Швидке сортування (або сортування Хоора) полягає в тому, що зі списку
обирають один елемент, а всі інші елементи поділяють на дві частини. В першу
частину збирають елементи, які менші обраного, в другу збирають елементи, які
більші за обраний. Потім кожну частину швидко сортують і поєднують в кінце-
вий список в такій послідовності: перша впорядкована частина, обраний елемент,
друга впорядкована частина.

51
Визначте функцію qsort, яка впорядковує список за зростанням його елеме-
нтів.
Вирішення задачі
Почнемо з прикладу і впорядкуємо список [5,3,7,4,8,1,6,2]. Обраним будем
вважати початковий елемент, в нашому випадку це 5.
Першу частину складе список [3,4,1,2], другу – список [7,8,6]. Після сорту-
вання ці частини стануть [1,2,3,4] і [6,7,8] відповідно. Нарешті все поєднаємо –
[1,2,3,4] ++ [5] ++ [6,7,8].
Відбирати елементи в першу і другу частину можна шляхом фільтрації,
поєднувати – за допомогою операції (++).
qsort (x:xs) = qsort (filter (< x) xs) ++
[x] ++
qsort (filter (> x) xs)
У нашому визначенні функції qsort бракує бази рекурсії. Додамо її.
qsort [] = []
Випробуємо наше рішення.
> qsort [5,3,7,4,8,1,6,2]
[1,2,3,4,5,6,7,8]
> qsort [3,4,2,5,2,4]
[2,3,4,5]
Видно, що коли в списку є елементи, що повторюються, то наша функція
не лише впорядковує, а і видаляє дублікати, а такого ніхто не замовляв. Цю по-
милку легко виправити, будь ласка, зробіть це самі.

10.3 Приклади аудиторних і домашніх задач

1. Переробіть рішення задачі «Видалити входження» так, щоб функція del


видаляла тільки перше входження елемента у список.
2. Якщо підрахувати суму цифр цілого числа, вийде ціле число, суму цифр
якого також можна підрахувати. Так можна продовжувати, доки не вийде одно-
разрядне ціле число, тут доведеться зупинитись.
52
Визначте функцію total, яка зробить такий підрахунок.
> total 245
2
> total 123123123
9
3. Запропонуйте власну реалізацію операції (++) для списків. Назвіть опе-
рацію (+++).
> [1,2] +++ [3,4]
[1,2,3,4]
4. Визначте функцію
hex2str :: Int -> String
яка отримує ціле додатне число та повертає рядок з його шістнадцятковим запи-
сом.
> hex2str 300
"12C"
5. Вирішить на Гаскелі задачу про ханойські вежі. Для цього визначте фу-
нкцію hanoj:: Int->[(Int, Int)], яка отримує число дисків вежі на першому стрижні
і повертає послідовність перекладання всієї вежі на третій стрижень у вигляді
списку пар.
Перший елемент пари – номер стрижня-джерела, другий елемент пари –
номер стрижня-приймальника.
> hanoj 3
[(1,3),(1,2),(3,2),(1,3),(2,1),(2,3),(1,3)].

53
ПЕРЕЛІК ДЖЕРЕЛ ПОСИЛАННЯ

1. Бондарєв В.М. Електронний учбовий посібник з дисципліни «Основи


програмування» URL: http://tss.co.ua:5555/ (дата звернення: 20.09.2022).
2. JavaScript in Visual Studio Code. URL:
https://code.visualstudio.com/docs/languages/javascript (дата звернення:
20.09.2022).
3. Життя (гра) / Вікіпедія, Вільна енциклопедія. URL:
https://uk.wikipedia.org/wiki/Життя_(гра) (дата звернення: 20.09.2022).
4. Minesweeper (video game) / Wikipedia. The Free Encyclopedia. URL:
https://en.wikipedia.org/wiki/Minesweeper_(video_game) (дата звернення:
20.09.2022).

54
Навчальне видання

МЕТОДИЧНІ ВКАЗІВКИ
до практичних занять з дисципліни

«ОСНОВИ ПРОГРАМУВАННЯ»
для студентів першого (бакалаврського) рівня вищої освіти
усіх форм навчання
спеціальності 121 – «Інженерія програмного забезпечення»
освітньо-професійна програма «Програмна інженерія»

Упорядники: БОНДАРЄВ Володимир Михайлович


ЧЕРЕПАНОВА Юлія Юріївна

Відповідальний випусковий З.В. Дудар


Редактор
Комп’ютерна верстка

План 2022 , поз. ____


Підп. до друку ___.___.22. Формат 60х 84 1/16. Спосіб друку – ризографія.
Умов. друк.арк. ____. Облік.вид.арк. . Тираж _____ прим.
Зам. № ___ Ціна договірна.

ХНУРЕ, 61166, Харків, просп. Науки, 14

Віддруковано у редакційно-видавничому відділі ХНУРЕ


Харків, просп. Науки, 14
55

You might also like