Professional Documents
Culture Documents
ОПР ПЗ
ОПР ПЗ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ
УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
МЕТОДИЧНІ ВКАЗІВКИ
до практичних занять з дисципліни
«ОСНОВИ ПРОГРАМУВАННЯ»
Харків 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
ВСТУП
6
1 КОМАНДИ УМОВНА ТА ПОВТОРЕННЯ
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);
12
2 ФУНКЦІЇ
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
}
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
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>
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>
20
<input type="button" id="findButton"
value="Знайти НОД" />
</div>
<div id="result"></div>
<script src="gcd.js"></script>
</body>
</html>
21
Зауважимо, що завдяки деяким рисам JS, увесь код третього пункту можна
скороти до наступного.
findButton.addEventListener('click', function() {
result.innerHTML = gcd(first.value, second.value);
});
4 МАСИВИ
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;
одразу після циклу.
Після всякої зміни в програмі зазвичай повторюють усі тести. У нас їх було
два – перший перевіряв загальний випадок, другий – один з крайніх випадків.
Було б доцільно добавити інший крайній випадок, коли додаткове число більше
за найбільший елемент масиву, і тести на співпадіння додаткового числа з сере-
дніми та крайніми елементами.
Сподіваємось, ви це зробите самостійно.
5 СЕРЕДОВИЩЕ РОЗРОБКИ
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 буде зупинятися в контрольних точках, якщо ви їх поставите.
6 РЯДКИ
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);
7 ПОСИЛАЛЬНІ ТИПИ
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)
}
37
3. Визначте функцію myShift(масив), яка зробить те саме, що метод масиву
shift.
Не використовуйте метод shift.
4. Визначте функцію myUnshift(масив, значення), яка зробить те саме, що
метод масиву unshift.
Не використовуйте метод unshift.
8 ОБ’ЄКТИ
Конструювання об'єктів
Об’єкти ідеально підходять для програмного втілення різноманітних сут-
ностей предметної області.
Наприклад, сутність Студент має такі властивості: ім’я, група, рейтинг ус-
пішності, хобі (ми усвідомлюємо, що в реалі все набагато складніше, але спро-
щуємо для короткості прикладів).
Студент Шевченко може бути представлений у програмі наступним
об’єктом.
let andriy = {
name: "Шевченко",
38
group: "ПЗПІ-22-10",
rating: 66,
hobby: "футбол" };
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);
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.
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 Приклади аудиторних і домашніх задач
9 РЕКУРСІЯ
45
3) долучити перший елемент до кожної створеної перестановки.
Так само вчинимо з усіма іншими елементами первісного масиву і отрима-
ємо всі інші сукупності.
На завершення поєднаємо сукупності в одному масиві, який і стане резуль-
татом роботи в цілому.
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;
}
48
6. Визначте рекурсивну функцію reverse, яка отримує зв'язаний список і
створює інший список, в якому корисне навантаження розташовано у зворот-
ному порядку
> reverse( {load:10, next:{ load:20, next:null}} )
{ load:20, next:{ load:10, next:null}}
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]
Видно, що коли в списку є елементи, що повторюються, то наша функція
не лише впорядковує, а і видаляє дублікати, а такого ніхто не замовляв. Цю по-
милку легко виправити, будь ласка, зробіть це самі.
53
ПЕРЕЛІК ДЖЕРЕЛ ПОСИЛАННЯ
54
Навчальне видання
МЕТОДИЧНІ ВКАЗІВКИ
до практичних занять з дисципліни
«ОСНОВИ ПРОГРАМУВАННЯ»
для студентів першого (бакалаврського) рівня вищої освіти
усіх форм навчання
спеціальності 121 – «Інженерія програмного забезпечення»
освітньо-професійна програма «Програмна інженерія»