Professional Documents
Culture Documents
Бичков фінал
Бичков фінал
О. С. Бичков
М. В. Ткаченко
Практичне
програмування
мовою Python
Пiдручник
УДК 519.85:004.43(075.8)
Б67
Рецензенти:
д-р техн. наук, проф. В . В . В и ш н е в с ь к и й ,
д-р техн. наук, проф. В . В . О н и щ е н к о
Бичков О. С.
Б67 Практичне програмування мовою Python : підручник
/ О. С. Бичков, М. В. Ткаченко. – К. : ВПЦ "Київський
університет", 2019. – 365 с.
ISBN 978-966-933-064-2
УДК 519.85:004.43(075.8)
2
CGI (Common Gateway Interface – "загальний інтерфейс шлюзу") – ста-
ндарт інтерфейсу, що використовується для зв'язку зовнішньої програми з
веб-сервером.
3
FTP (File Transfer Protocol) – один з базових протоколів передачі
файлів, призначений для передачі файлів у мережі між комп'ютерами.
4
XML (Extensible Markup Language) – розширювана мова розмітки.
5
єю, GUI8 та іншими системно-залежними процесами та об'єкта-
ми. Пам'ятайте, що все це є на кожному кроці, де встановлена
Python. У цьому полягає філософія Python: "Усе включено" (All
inclusive).
Крім стандартної бібліотеки, існує безліч інших високоякіс-
них бібліотек, які можна знайти в каталозі пакетів Python.
Фраза Переклад
1. Beautiful is better than ugly Красиве краще за потворне
2. Explicit is better than implicit Просте краще за складне
3. Complex is better than
Складне краще за ускладнене
complicated
4. Flat is better than nested Плоске краще ніж вкладене
5. Sparse is better than dense Розріджене краще ніж щільне
6. Readability counts Читабельність важлива
7. Special cases aren't special Виняткові випадки не настільки
enough to break the rules важливі, щоб порушувати правила
Однак практичність важливіша
8. Although practicality beats purity.
за чистоту
Помилки ніколи не мають замо-
9. Errors should never pass silently
вчуватися
5
XML-RPC (Extensible Markup Language Remote Procedure Call – XML-
виклик віддалених процедур) – визначає набір стандартних типів даних і
команд, які програміст може використовувати для доступу до функціона-
льності іншої програми, що міститься на іншому комп'ютері в мережі.
6
HTML (Hyper Text Markup Language – мова розмітки гіпертексто-
вих документів) – стандартна мова розмітки веб-сторінок в Інтернеті.
7
WAV (wave) – формат аудіофайлу, розроблений компаніями Mi-
crosoft та IBM.
8
GUI (Graphical user interface – графічний інтерфейс користувача) –
тип інтерфейсу, який дозволяє користувачам взаємодіяти з електро-
нними пристроями через графічні зображення та візуальні вказівки.
6
Фраза Переклад
За винятком замовчування, яке
10. Unless explicitly silenced
задане спеціально
11. In the face of ambiguity, refuse У випадку неоднозначності не
the temptation to guess піддавайтеся спокусі вгадати
12. There should be one – and prefe- Має існувати один і, бажано, тільки
rably only one – obvious way to do it один очевидний спосіб зробити це
Хоча він може бути з першого
13. Although that way may not be
погляду не очевидний, якщо ти
obvious at first unless you're Dutch
не голландець
14. Now is better than never Зараз краще, ніж ніколи
15. Although never is often better Проте ніколи частіше краще, ніж
than * Right * now прямо зараз
16. If the implementation is hard to Якщо реалізацію складно пояс-
explain, it's a bad idea нити – це погана ідея
17. If the implementation is easy to Якщо реалізацію легко пояснити
explain, it may be a good idea – це може бути хороша ідея
18. Namespaces are one honking Простори назв – прекрасна ідея,
great idea – Let's do more of those! давайте робити їх більше!
9
"#" – знак решітки. Інші назви: "ґратка", "геш"/"хеш", "дієз", "шарп",
"знак номера", "клоп".
7
Намагайтеся у своїх програмах писати якомога більше кори-
сних коментарів, що пояснюють:
• припущення;
• важливі розв'язання;
• важливі деталі;
• проблеми, які ви намагаєтеся розв'язати;
• проблеми, яких ви намагаєтеся уникнути тощо.
Текст програми пояснює ЯК, а коментарі мають пояснювати
ЧОМУ. Це буде корисним для тих, хто читатиме вашу програму,
оскільки їм легше буде зрозуміти, що програма робить.
1.3.3. Числа
8
1.3.4. Рядки
10
"\" – back slash – обернена коса риска.
9
Метод format. Призначений для виведення форматованих
даних. Розглянемо такий код:
age = 26
name = 'Swaroop'
print('Вік {0} – {1} років.'.format(name, age))
print('Чому {0} бавиться з цим Python?'.format(name))
Виведення:
$ python str_format.py
Вік Swaroop – 26 років.
Чому Swaroop бавиться з цим Python?
10
1.3.5. Змінні
1.3.6. Об'єкти
11
ASCII (American Standard Code for Information Interchange – Аме-
риканський стандартний код для інформаційного обміну) – система
кодів, у якій числа від 0 до 127 включно поставлені у відповідність до
літер, цифр і символів пунктуації.
12
Unicode (Юніко́д), УНІфіковане КОДування – промисловий стан-
дарт, розроблений, щоб забезпечити цифрове зображення символів
усіх писемностей світу та спеціальних символів.
11
Розглянемо такий приклад використання змінних і констант:
i=5
print(i)
i=i+1#i+=1
print(i)
s = '''Це багаторядковий рядок.
Це другий її рядок.'''
print(s)
Виведення:
5
6
Це багаторядковий рядок.
Це другий її рядок.
Як це працює. Спочатку присвоюємо значення константи 5
змінній i, використовуючи оператор присвоювання (=). Цей ря-
док називається пропозицією й указує, що має бути виконана
деяка дія. У цьому випадку ми пов'язуємо ім'я змінної зі значен-
ням 5. Потім друкуємо значення i, використовуючи функцію
print, яка просто друкує значення змінної на екрані.
Далі додаємо 1 до значення, що зберігається в i, та зберігаємо
його там. Після цього друкуємо його та отримуємо значення 6,
що не дивно. Аналогічно присвоюємо рядкову константу змін-
ній s, після чого друкуємо.
Зауваження для програмістів мовою C++: змінні використо-
вуються простим присвоюванням їм значень. Ніякого попере-
днього оголошення або визначення типу даних не потрібно (ди-
намічне визначення типу змінних).
Розглянемо особливості роботи Python зі змінними на при-
кладі такого коду (переведення зі шкали градусів за Цельсієм у
шкалу градусів за Фаренгейтом):
>>> cel = 26
>>> cel
26 cel id1 : int
>>> 9/5 * cel + 32
78.80000000000001 id1 26
>>>
12
У момент виконання присвоювання cel = 26 у пам'яті комп'ю-
тера створюється об'єкт, розміщений за деякою адресою13 (умо-
вно позначимо його id1), що має значення 26 цілочислового
типу int. Потім створюється змінна з ім'ям cel, якій присвоюєть-
ся адреса об'єкта id1. Змінні в Python містять адреси об'єктів,
інакше можна сказати, що змінні посилаються на об'єкти. По-
стійно зберігаючи в пам'яті цю модель, для спрощення будемо
говорити, що змінна містить значення.
Обчислення наступного виразу зумовить присвоювання
змінній cel значення 72, тобто спочатку обчислюється права
частина, потім результат присвоюється лівій частині:
>>> cel = 26 + 46
>>> cel
72
>>>
Розглянемо трохи складніший приклад. Замість змінної diff
підставимо цілочислове значення 20:
>>> diff = 20
>>> double = 2 * diff
>>> double
40
>>>
Після закінчення обчислень пам'ять для Python матиме такий
вигляд (рис. 1.1):
Рис. 1.1
13
Інформація для досвідчених програмістів. Функція id () повертає
ідентифікатор об'єкта, переданого як аргумент функції. У реалізації
Python число, що повертається, є адресою об'єкта в пам'яті.
13
Продовжимо обчислення. Дамо змінній diff значення 5 і роз-
глянемо зміст змінних double та diff.
>>> diff = 5
>>> double
40
>>> diff
5
У момент присвоювання змінній diff значення 5 у пам'яті ство-
риться об'єкт за адресою id3, що містить цілочислове значення 5.
Після цього зміниться зміст змінної diff, замість адреси id1 туди
запишеться адреса id3 (рис. 1.2). Також Python побачить, що на
об'єкт за адресою id1 більше ніхто не посилається, і видалить його
з пам'яті14 (автоматичне "прибирання" сміття). При цьому Python
не змінюватиме існуючі числові об'єкти, а створить нові15 (особли-
вість числового типу даних – його об'єкти є незмінними).
14
Поточну кількість посилань на об'єкт можна дізнатися за допомогою
функції
sys.getrefcount ():
>>> s=36
>>> import sys
>>> sys.getrefcount(s)
13
>>>
15
Інформація для досвідчених програмістів. Для економії ресурсів при
роботі з невеликими цілими значеннями Python посилається на вже існую-
чі в пам'яті об'єкти:
>>> i=3
>>> j=3
>>> k=4-1
>>> id(i)
1647399712
>>> id(j)
1647399712
>>> id(k)
1647399712
>>> i=3000000000
>>> j=3000000000
>>> id(i)
13786168
>>> id(j)
49195536
>>>
14
Рис. 1.2
15
Можна використовувати більше одного фізичного рядка для
логічного рядка, але до цього слід удаватися лише в разі дуже
довгих рядків. Приклад написання одного логічного рядка, що
займає кілька фізичних рядків, наведено нижче. Це називається
явним об'єднанням рядків.
s = 'Це рядок. \
Це рядок продовжується.'
print(s)
Маємо результат:
Це рядок. Це рядок продовжується.
Аналогічно
print \
(i)
є тим самим, що
print (i)
Іноді має місце неявне використання оберненої косої риски
("\"). Це стосується випадків, коли в логічному рядку є відкрита
кругла ("("), квадратна ("[") або фігурна ("{") дужка, але немає
дужки, що закриває. Така ситуація називається неявним об'єд-
нанням рядків.
1.3.8. Відступи
16
˽print ('Значення становить ', i)# Помилка! Пробіл на початку
рядка
^
IndentationError: unexpected indent
Зверніть увагу, що на початку другого рядка є один пробіл
("˽"). Помилка, відображена Python, свідчить про те, що синтак-
сис програми є неправильним, тобто програма не була написана
за правилами. Це означає, що ви не можете починати нові блоки
речень де завгодно (крім основного блоку за умовчанням, який
використовується в усій програмі).
Вправи
1. Арифметичні операції над двома числами. Програма вико-
нує різні математичні дії над двома числами, виводячи на екран
розгорнуту форму запису дії.
На вході: два цілих додатних числа – А та В.
На виході: рядок, що містить цілі (або дійсні) числа:
1.4.1. Оператори
1.4.4. Асоціативність
Вправи
1. Сума цифр. Програма обчислює суму цифр s у заданому
числі n.
На вході: ціле тризначне число n.
На виході: ціле число s (min ≤ n ≤ max). Додатково визначити
значення min та max.
Введіть число: 456 Сума цифр числа 456 становить 15.
Min <= 15 <= Max
2. Переведення бітів. Програма переводить ціле число бітів b
у запис типу: "х1 Мбайт х2 Кбайт х3 байт х4 біт".
На вході: ціле число бітів b (наприклад 1234567).
На виході: рядок, що містить цілі числа.
Випадкове число бітів: 1234567 1 Мбайт, 234 Кбайт, 560 байт,
7 біт
Випадкове число бітів: 2345671 2 Мбайт, 345 Кбайт, 670 байт,
1 біт
21
3. Переведення секунд. Програма переводить ціле число се-
кунд s у запис типу: "x1 хвилина х2 секунда".
На вході: ціле число секунд s (1000 ≤ n < 3600).
На виході: рядок, що містить цілі числа.
Випадкове число секунд: 1234 20 хвилин, 34 секунди
4. Електронні годинники. З початку доби минуло n хвилин.
Програма визначає, скільки годин h і хвилин m буде показувати
електронний годинник у цей момент.
Урахуйте, що число n може бути більше кількості хвилин у добі.
На вході: ціле число n (60 < n < 2000).
На виході: рядок, що містить цілі числа h та m (0 ≤ h, m < 60),
роздільник – двокрапка. Якщо значення менше 10, то місце дру-
гої цифри заповнюється нулем.
Випадкове число хвилин: 648 10:48
Випадкове число хвилин: 1505 01:05
5. Різниця часів. Дано значення двох моментів часу, що нале-
жать одній добі: години h1 та h2, хвилини m1 та m2, секунди s1 та
s2, причому другий момент часу настав не раніше першого. Про-
грама визначає, скільки секунд минуло між двома моментами.
На вході: шість цілих чисел: три (h1, m1, s1) для першого і
три (h2, m2, s2) для другого моментів (0≤h1, h2; 0≤m1, m2≤59;
0≤s1, s2≤59).
На виході: ціле число – кількість секунд.
Час початку відліку: 1 1 1 3661 сек
Час кінця відліку: 2 2 2
Час початку відліку: 1 2 30 50 сек
Час кінця відліку: 1 3 20
6. Годинники-1. З початку доби минуло h годин, m хвилин, s
секунд. Програма обчислює кут α (у градусах), на який поверну-
лась годинникова стрілка з початку доби:
m s 360
a = (h + + )× .
60 3600 12
На вході: три цілих числа h, m, s (випадкові) (0≤ h <12, 0≤ m
<60, 0≤ s <60).
22
На виході: одне дійсне число α.
Введіть h, m, s: 1 2 6 31.05 град.
7. Годинники-2. З початку доби годинникова стрілка повер-
нулася на кут αо. Програма визначає, скільки повних годин h,
хвилин m і секунд s минуло з початку доби.
На вході: одне дійсне число α (випадкове) (0≤ α <360).
На виході: рядок, що містить час у форматі "гг: мм: сс".
Введіть α (град.): 31.05 01:02:06
Введіть α (град.): 359.992 11:59:59
Введіть α (град.): 1 00:02:00
23
True
>>> x == 5
False
>>> x != 5 # x не дорівнює 5
True
>>> x > 5 # x більше 5
False
>>> x < 5 # x менше 5
True
>>> x >= 4 # x більше або дорівнює 4
True
>>> x <= 4 # x менше або дорівнює 4
True
Результат порівняння двох значень ми можемо записати в
змінну:
>>> y = x == 5
>>> print y
False
Бачимо, що пріоритет операцій порівняння менше пріоритету
арифметичних операцій, але більше, ніж в операції присвоювання.
24
Для простоти складемо таблицю зі списків усіх можливих ком-
бінацій значень операндів логічних виразів і самих виразів (такі
таблиці в математичній логіці називаються таблицями істинності):
х у x and y x or y
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 1
25
positive. Далі йде ще один оператор if, що перевіряє виконання
умови x < 0, яке є хибним, тому тіло цього умовного оператора
Python пропустив і виконав наступну інструкцію.
26
function1()
elif choice == "2":
function2()
else:
print "Invalid choice!"
print "Thank you."
Спочатку програма просить користувача ввести одиницю або
двійку, потім умовний оператор перевіряє введене значення на
рівність із рядком '1' (функція raw_input () повертає рядкове зна-
чення). Якщо умова істинна, то викликається функція function1
(), після чого програма виводить рядок "Thank you." і завершу-
ється; в іншому випадку значення змінної choice порівнюється з
рядком '2': якщо умова виконується, то викликається функція
function2 (), потім виводиться рядок "Thank you." і програма
завершується; інакше програма виводить повідомлення про те,
що введене значення некоректне, потім – подяку і завершується.
В англомовній літературі такі розгалужені умовні оператори
зазвичай називають chained conditionals, тобто ланцюжками умов.
29
... print "x is between 1 and 10"
...
x is between 1 and 10
>>>
Вправи
1. Шахова дошка. Програма визначає, чи пофарбовані кліти-
ни на шаховій дошці в один колір, за їх координатами х1 та у1,
х2 та у2, що задають номер стовпця і номер рядка спочатку для
першої клітини, потім для другої.
На вході: чотири цілих числа (1 ≤ х1, у1, х2, у2 ≤ 8).
На виході: одно з двох рядкових значень: 'YES' або 'NO'.
Введіть координати 1 і 2 клітини: 1 1 2 6 YES
Введіть координати 1 і 2 клітини: 1 3 8 7 NO
2. Шахова тура. Програма визначає, чи може тура потрапити
з першої клітини на другу одним ходом за координатами х1 та
у1, х2 та у2, що задають номер стовпчика і номер рядка спочат-
ку для першої клітини, потім для другої. Шахова тура ходить по
горизонталі або вертикалі.
На вході: чотири цілих числа (1≤ х1, у1, х2, у2 ≤ 8).
На виході: одно з двох рядкових значень: 'YES' або 'NO'.
Введіть координати 1 і 2 клітини: 1 1 1 8 YES
Введіть координати 1 і 2 клітини: 4 4 5 5 NO
3. Шаховий слон. Програма визначає, чи може слон потрапи-
ти з першої клітини на другу одним ходом за координатами х1
та у1, х2 та у2, що задають номер стовпця і номер рядка спочат-
ку для першої клітини, потім для другої. Шаховий слон ходить
по діагоналі.
На вході: чотири цілих числа (1≤ х1, у1, х2, у2 ≤ 8).
На виході: одно з двох рядкових значень: 'YES' або 'NO'.
Введіть координати 1 і 2 клітини: 1 1 1 8 NO
Введіть координати 1 і 2 клітини: 4 4 5 5 YES
4. Шаховий кінь. Програма визначає, чи може кінь потрапити
з першої клітини на другу одним ходом за координатами х1 та
у1, х2 та у2, що задають номер стовпчика і номер рядка спочат-
30
ку для першої клітини, потім для другої. Шаховий кінь ходить
літерою "Г" – на дві клітини з вертикалі в будь-якому напрямку і
на одну – по горизонталі, або навпаки.
На вході: чотири цілих числа (1≤ х1, у1, х2, у2 ≤ 8).
На виході: одно з двох рядкових значень: 'YES' або 'NO'.
Введіть координати 1 і 2 клітини: 2 4 3 2 YES
Введіть координати 1 і 2 клітини: 2 8 3 7 NO
1.6.1. Лічильники
36
1.6.5. Вкладені оператори циклу
і двовимірні таблиці
38
1.6.7. Функція генератора
У попередніх прикладах ми вже зустрічалися з функцією
range (), яка генерує послідовність цілих чисел без необхідності
створення всієї послідовності та її зберігання в пам'яті.
Напишемо власну функцію-генератор, яка відрізняється тим,
що замість return використовує інструкцію yield:
>>> def my_range(first=0, last=10, step=1):
number = first
while number < last:
yield number
number += step
>>> my_range
<function my_range at 0x02DF6228>
>>> ranger = my_range(1, 5)
Бачимо, що функція my_range () повернула об'єкт генератора:
>>> ranger
<generator object my_range at 0x02E00450>
>>> for i in ranger:
print(i)
1
2
3
4
Вправи
1. Заробітна платня. Працівник заробляє X гривень за 38 год
роботи. Йому платять у 1,5 раза більше за кожну годину понад
38 год. Яку суму він отримає, якщо відпрацює A годин?
На вході: два цілих числа, розділених пробілом (Х А).
На виході: ціле число.
38 1 38 грн
38 40 41 грн
2. Банківські відсотки. Внесок у банк становить x грн. Щорі-
чно він збільшується на p відсотків, після чого дрібна частина
(копійки) відкидається. Визначте, за скільки років внесок стано-
витиме не менше y грн.
39
На вході: три натуральних числа: x, p, y, розділених пробілом.
На виході: ціле число.
100 10 200 8 років
112 100 років
100 1 200 70 років
400 7 1046 15 років
πx
f ( x) = lg(3) + x 5sin( ).
3
5. Знайти всі числа за умовою. Знайдіть усі п'ятизначні числа,
які при діленні на 133 дають у залишку 125, а при діленні на 134
дають у залишку 111.
19809 37631 55453 73275 91097
40
1.7. Цикли. Оператор циклу for
Оператор for..in також є оператором циклу, який здійснює
ітерацію за послідовністю об'єктів, тобто проходить через кожен
елемент у послідовності (упорядкований набір елементів).
for i in range(1, 5):
print(i)
else:
print('Цикл for закончен')
Виведення:
1
2
3
4
Цикл for закінчено.
Як це працює. У програмі ми виводимо на екран послідов-
ність чисел. Генеруємо цю послідовність, використовуючи вбу-
довану функцію range.
Задаємо два числа, і range повертає послідовність чисел від
першого числа до другого. Наприклад, range (1,5) дає послідов-
ність [1, 2, 3, 4]. За умовчанням range набуває значення кроку,
що дорівнює 1. Якщо ми поставимо також третє число range, то
воно буде кроком. Наприклад, range (1,5,2) дасть [1,3]. Пам'я-
тайте, інтервал простягається тільки до другого числа, тобто не
включає його в себе.
Зверніть увагу, що range () генерує послідовність чисел, але
тільки по одному числу за раз – коли оператор for запитує на-
ступний елемент. Щоб побачити всю послідовність чисел відра-
зу, використовуйте list (range ()).
Потім цикл for здійснює ітерацію з цього діапазону – for i in
range (1,5) еквівалентно for i in [1, 2, 3, 4], що нагадує присвою-
вання змінній i по одному числу (або об'єкту) за раз, виконуючи
блок команд для кожного значення i. У даному випадку в блоці
команд ми просто виводимо значення на екран.
Пам'ятайте, що блок else не обов'язковий. Якщо він присут-
ній, то завжди виконується один раз після закінчення циклу for,
якщо тільки не вказано оператор break.
41
Цикл for..in працює для будь-якої послідовності. У нашому
випадку – це список чисел, генерований вбудованою функцією
range, але в загальному випадку можна використовувати будь-
яку послідовність будь-яких об'єктів!
Примітка для програмістів мовами C/C++/Java/C#: цикл for у
Python радикально відрізняється від циклу for у C/C++. Програ-
місти мовою C # помітять, що цикл for у Python схожий на цикл
foreach у C #. Програмістам мовою Java це може нагадати конс-
трукцію for (int i: IntArray).
Якщо в C/C++ записати for (int i = 0; i <5; i ++), то в Python
цьому відповідатиме вираз for i in range (0,5). Отже, у Python
цикл for простіший, виразніший і менше схильний до помилок.
Вправи
1. Ряд. Напишіть програму, що виводить на екран усі числа
від А до В включно за зростанням, якщо A <B, або за спаданням
– в іншому випадку.
На вході: у першому рядку вводиться А, у другому – B.
На виході: послідовність цілих чисел, розділених пробілом.
Введіть А: 1
1 2 3 4 5 6 7 8 9 10
Введіть В: 10
Введіть А: 10
10 9 8 7 6 5 4 3 2 1
Введіть В: 1
2. Непарні числа. Напишіть програму, що виводить на екран
усі непарні числа від А до В включно (А≤В).
На вході: два цілих числа, розділених пробілом.
На виході: послідовність цілих чисел, розділених пробілом.
Введіть два числа: 1 7 1357
Введіть два числа: -18 -29 -19 -21 -23 -25 -27 -29
3. Драбинка. За даним натуральним n ≤ 9 виведіть драбинку
із n сходинок, де i-та сходинка складається із чисел від 1 до i без
пробілів.
На вході: ціле число n.
На виході: цілі числа.
1
Введіть n: 3 12
123
Введіть n: 1 1
4. Таблиця множення. Скласти програму, що виводить таб-
лицю множення із заданим першим і другим числом від 1 до 9.
На вході: два цілих числа, розділених пробілом.
44
На виході: таблиця множення.
1х1=1|1х2=2|1х3=3
Введіть два числа: 1 3 2х1=2|2х2=4|2х3=6
3x1=3|3x2=6|3x3=9
Введіть два числа: 4 4 4 x 1 = 4 | 4 x 2 = 8 | 4 x 3 = 12 | 4 x 4 = 16
5. Кількість нулів у числі. Напишіть програму, яка обчислює
кількість нулів у заданому числі (N < 1Е11).
На вході: ціле число N.
На виході: ціле число.
Введіть n: 103 235 045 207 3
Введіть n: 10 984 1
1.8.1. Функції
Функції – це багаторазово використовувані фрагменти про-
грами. Вони дозволяють дати ім'я певному блоку команд із тим,
щоб надалі запускати цей блок за вказаним іменем у будь-якому
місці програми скільки завгодно разів. Це називається викликом
функції. Результат виклику функції можна присвоїти змінній,
використовувати його як операнди математичних виразів, тобто
складати складніші вирази.
Розглянемо кілька популярних математичних функцій
мови Python.
pow (x, y) повертає значення x у степені y. Еквівалентно за-
пису x ** y.
>>> pow(4,5)
1024
>>>
round (number) повертає число з плаваючою точкою, округ-
лене до 0 цифр після коми (за умовчанням). Може бути викли-
кана з двома аргументами:
round (number [, ndigits]), де ndigits – кількість знаків після коми.
45
>>> round(4.56666)
5
>>> round(4.56666, 3)
4.567
>>>
Крім створення складних математичних виразів, Python до-
зволяє передавати результати виконання функцій як аргументів
інших функцій без використання додаткових змінних (рис. 1.3):
Рис. 1.3
46
Функції визначаються за допомогою зарезервованого слова
def. Після цього слова вказується ім'я функції, потім – пара ду-
жок, у яких можна вказати імена деяких змінних, і двокрапка в
кінці рядка. Далі йде блок команд, що складають функцію:
def sayHello():
print('Привіт, Світ!') # блок, що належить функції
# Кінець функції
sayHello() # виклик функції
sayHello() # ще один виклик функції
Виведення:
Привіт, Світ!
Привіт, Світ!
Як це працює. Ми визначили функцію з ім'ям sayHello, вико-
ристовуючи описаний вище синтаксис. Ця функція не приймає
параметрів, тому в дужках не оголошені будь-які змінні. Пара-
метри функції – це такі вхідні дані, які можна передати функції,
щоб отримати відповідний до них результат.
Зверніть увагу, що можна викликати ту саму функцію багато ра-
зів, а значить, немає необхідності писати той самий код знову і знову.
47
elif a == b:
print(a, 'дорівнює', b)
else:
print(b, 'максимально')
printMax(3, 4) # пряма передача значень
x=5
y=7
printMax(x, y) # передача змінних як аргументів
Виведення:
4 максимально
7 максимально
Як це працює. Тут ми визначили функцію з ім'ям printMax,
яка використовує два параметри з іменами a та b. Ми знаходимо
найбільше число із застосуванням простого оператора if..else і
виводимо це число.
При першому виклику функції printMax ми безпосередньо пе-
редаємо числа як аргументи. У другому випадку викликаємо фу-
нкцію зі змінними як аргументами. printMax (x, y) призначає зна-
чення аргументу x параметру a, а значення аргументу y – параме-
тру b. В обох випадках функція printMax працює однаково.
49
def func():
global x
…
print('x дорівнює', x)
x=2
print('Замінюємо глобальне значення x на', x)
func()
print('Значення x становить', x)
Виведення:
x дорівнює 50
Замінюємо глобальне значення x на 2
Значення x становить 2
Як це працює. Зарезервоване слово global використовується
для того, щоб оголосити, що x – глобальна змінна, тобто коли ми
присвоюємо значення імені x усередині функції, ця зміна відобра-
зиться на значенні змінної x в основному блоці програми.
Використовуючи одне зарезервоване слово global, можна
оголосити відразу кілька змінних:
global x, y, z
51
Як це працює. Функція під ім'ям say використовується для
виведення на екран рядка вказану кількість разів. Якщо ми не
вказуємо значення, то за умовчанням рядок виводиться один
раз. Ми досягаємо цього зазначенням значення аргументу за
умовчанням, що дорівнює 1 для параметра times.
При першому виклику say ми вказуємо тільки рядок, і функція
виводить її один раз; при другому виклику вказуємо також аргумент
5, позначаючи, таким чином, що хочемо повторити фразу 5 разів.
1.8.9. Декоратори
54
print("Результат: ", result)
return result
return new_func
>>> def add_ints(a, b):
return a + b
>>> add_ints(3, 6)
9
>>> # ручне присвоювання декоратора:
>>> new_add_ints = doc_it(add_ints)
У результаті отримали нову функцію, яка містить додаткові
вирази, що додаються doc_it ():
>>> new_add_ints (3, 6)
Ім'я функції: add_ints
Позиційні аргументи: (3, 6)
Ключові аргументи: {}
Результат: 9
9
>>>
Спростити декорування (замість ручного присвоювання) мо-
жна за допомогою такої конструкції:
>>> @doc_it
def add_ints(a, b):
return a + b
>>> add_ints(3, 6) # add_ints = doc_it(add_ints)
Ім'я функції: add_ints
Позиційні аргументи: (3, 6)
Ключові аргументи: {}
Результат: 9
9
>>>
Визначимо ще один декоратор:
>>> def sq_it(func):
def new_func(*args, **kwargs):
result = func(*args, **kwargs)
55
return result * result
return new_func
Першим виконається декоратор sq_it (), потім doc_it ():
>>> @doc_it
@sq_it
def add_ints(a, b):
return a + b
>>> add_ints(3, 6)
Ім'я функції: new_func
Позиційні аргументи: (3, 6)
Ключові аргументи: {}
Результат: 81
81
>>>
Від зміни порядку декораторів результат не змінюється:
>>> @sq_it
@doc_it
def add_ints(a, b):
return a + b
>>> add_ints(3, 6)
Ім'я функції: add_ints
Позиційні аргументи: (3, 6)
Ключові аргументи: {}
Результат: 9
81
>>>
Приклад додавання тегів через виклик декораторів:
def bold(fun_hello):
def inner(who):
print ("<b>")
fun_hello(who)
print ("</b>")
return inner
def italic(fun_hello):
56
def inner(who):
print ("<i>")
fun_hello(who)
print ("</i>")
return inner
@italic
@bold
def hello(who):
print ("Hello", who)
hello("World")
Виведення:
<i>
<b>
Hello World
</b>
</i>
Припустимо, що необхідно перевірити швидкість роботи де-
якої функції:
import time
def timer(f):
def tmp(*args, **kwargs):
t = time.time()
res = f(*args, **kwargs)
print ("Час виконання функції: %f" % (time.time()-t))
return res
return tmp
# декоратор, який виконує затримку переданої функції на 1
секунду
def pause(f):
def tmp(*args, **kwargs):
time.sleep(1)
return f(*args, **kwargs)
return tmp
57
@timer
@pause
def func(x, y):
return x + y
func(1, 2)
Виведення:
Час виконання функції: 1.000044
Вправи
1. Кількість цифр числа. Написати програму з використанням
функції, що знаходить кількість C цифр цілого додатного числа
K (104 ≤ К ≤ 106), а також їхню суму S. За допомогою цієї про-
грами знайти кількість і суму цифр для кожного з п'яти випад-
кових цілих чисел. Рядкові змінні не використовувати.
На виході: випадкове ціле число і два цілих числа, розділених
пробілом.
12345 5 15
67890 5 30
13579 5 25
24680 5 20
98765 5 35
2. Інвертування числа. Написати програму з використанням
функції, яка міняє порядок проходження цифр цілого додатно-
го числа K (104 ≤ К ≤ 106) на зворотний. За допомогою цієї про-
60
грами поміняти порядок проходження цифр на зворотний
для кожного з п'яти випадкових цілих чисел. Рядкові змінні
не використовувати.
На виході: випадкове ціле число та інвертоване ціле число,
розділені пробілом.
12345 54321
67890 9876
13579 97531
24680 8642
98765 56789
18
Ініціалізація – дії, що виконуються при початковому завантаженні.
65
Змінна sys.argv є списком рядків. Вона містить список аргу-
ментів командного рядка, тобто аргументів, переданих програмі
з командного рядка.
У нашому прикладі, коли ми запускаємо "python using_sys.py
we are arguments", ми запускаємо модуль using_sys.py командою
python, а все, що потрібно далі – аргументи, які передаються
програмі. Python зберігає аргументи командного рядка в змінній
sys.argv для подальшого використання.
Пам'ятайте, що ім'я сценарію19, який запускається, завжди є
першим аргументом у списку sys.argv. Отже, у наведеному при-
кладі 'using_sys.py' буде елементом sys.argv [0], 'we' – sys.argv
[1], 'are' – sys.argv [2], а 'Arguments' – sys.argv [3]. Пам'ятайте, що
в Python нумерація починається з 0, а не з 1.
sys.path містить список імен каталогів, звідки імпортуються
модулі. Зауважте, що перший рядок у sys.path порожній, він пока-
зує, що поточна директорія також є частиною sys.path, яка збіга-
ється зі значенням змінного середовища PYTHONPATH. Це
означає, що модулі, розташовані в поточному каталозі, можна
імпортувати безпосередньо. В іншому випадку доведеться поміс-
тити свій модуль в один з каталогів, перерахованих у sys.path.
Пам'ятайте, що поточний каталог – це каталог, у якому була
запущена програма. Виконайте import os; print (os.getcwd ()),
щоб дізнатися поточний каталог програми.
19
Програму мовою програмування, що є інтерпретатором, також на-
зивають сценарієм, або скриптом.
66
1.9.2. Оператор from ... import ...
68
Як це працює. Зверніть увагу, що ми використовуємо те саме
позначення крапкою для доступу до елементів модуля. Python
скрізь використовує те саме позначення крапкою, надаючи йо-
му, таким чином, характерний вигляд і не змушуючи нас кожно-
го разу вивчати нові способи робити що-небудь.
Розглянемо версію, яка використовує синтаксис from..import:
from mymodule import sayhi, __version__
sayhi()
print('Версія', __version__)
Виведення:
mymodule_demo2.py такий самий, як і mymodule_demo.py.
Зверніть увагу: якщо в модулі, який імпортує даний модуль,
уже було оголошене ім'я __version__, то виникне конфлікт. Це
цілком можливо, оскільки оголошувати версію будь-якого мо-
дуля за допомогою зазначеного імені – загальноприйнята прак-
тика. Тому завжди рекомендується віддавати перевагу операто-
ру import, хоча це робить програму трохи довшою.
Також можна використовувати:
from mymodule import *
Це імпортує всі публічні імена, такі як sayhi, але не імпортує
__version__, тому що воно починається з подвійного підкреслення.
Вправи
1. Площа кільця. Написати програму з використанням функ-
ції, яка знаходить площу кільця, що визначається двома колами
із загальним центром і радіусами R1 та R2 (R1 та R2 – дійсні,
R1 > R2). За її допомогою знайти площі трьох кілець, для яких
зовнішні та внутрішні радіуси випадкові.
Значення π вважати таким, що дорівнює 3.14.
Розробити власний модуль (бібліотеку) з функцією, за допо-
могою якої розраховується площа кола.
На виході: два випадкових дійсних числа і площа кільця. Ви-
ведення даних здійснити у вигляді таблиці.
71
2. Сума випадкових чисел. Написати програму з використан-
ням функції цілого типу, яка знаходить суму всіх цілих чисел
від A до B включно. Якщо A > B, то функція повертає 0. За до-
помогою цієї функції знайти суми чисел від A до B і від B до C,
якщо дано 5 наборів випадкових чисел.
Розробити власний модуль (бібліотеку) з функцією, за допо-
могою якої розраховується сума відповідних чисел.
На виході: три цілих випадкових числа і дві суми. Виведення
даних здійснити у вигляді таблиці.
3. Квадрат числа. Написати програму з використанням функ-
ції, яка повертає TRUE, якщо цілий параметр K (0≤K≤100) є
квадратом деякого цілого числа, і FALSE – в іншому випадку. За
її допомогою знайти кількість квадратів у наборі з 10 цілих до-
датних випадкових чисел.
На виході: таблиця з даними.
4. Розмір кута в радіанах. Написати програму з використан-
ням модуля, яка знаходить величину кута в радіанах, якщо дано
його величину D у градусах. Значення π вважати таким, що до-
рівнює 3.14. За допомогою програми перевести з градусів у ра-
діани п'ять випадкових кутів.
На виході: таблиця з даними.
5. Подвійний факторіал. Написати програму з використанням
модуля, яка обчислює подвійний факторіал:
N !! = 1 • 3 • 5 •. . . • N, якщо N – непарне;
N !! = 2 • 4 • 6 •. . . • N, якщо N – парне.
За допомогою цієї програми знайти подвійні факторіали п'я-
ти випадкових чисел.
На виході: таблиця з даними.
74
Закінчення табл. 1.2
Синтаксис Опис
i&j Бітова операція AND (І) над цілими числами i та j
i << j Зсовує значення i ліворуч на j бітів аналогічно операції
i * (2 * * j) без перевірки на переповнення
i >> j Зсовує значення i праворуч на j бітів аналогічно опера-
ції i//(2 ** j) без перевірки на переповнення
~i Інвертує біти числа i
1.10.4. Рядки
78
а = " Тут 'апострофи' можна не екранувати, а \ "лапки \" дове-
деться."
b = 'Тут \' апострофи \ 'доведеться екранувати, а "лапки" не
обов'язково.'
У мові Python символ переведення рядка інтерпретується як
завершальний символ інструкції, але не всередині круглих (),
квадратних [], фігурних {} дужок і рядків у потрійних лапках.
Символи переведення рядка можуть використовуватися в рядках
у потрійних лапках, можна включати символи переведення ряд-
ка в будь-які рядкові літерали за допомогою екранованої послі-
довності \ n. Усі екрановані послідовності, допустимі в мові
Python, перераховані в табл. 1.4.
Таблиця 1.4
Екрановані послідовності в мові Python
Послідовність Значення
\переведення_рядка Екранує (тобто ігнорує) символ переведення рядка
\\ Символ зворотного слешу (\)
\' Апостроф (')
\” Лапки (“)
\а Символ ASCII "сигнал" (bell, BEL)
\b Символ ASCII "видалення" (backspace, BS)
\f Символ ASCII "переведення формату" (formfeed, FF)
\n Символ ASCII "переведення рядка" (linefeed, LF)
\N{назва} Символ Юнікоду із заданою назвою
\ооо Символ із заданим восьмеричним кодом
Символ повернення каретки ASCII (carriage
\r
return, CR)
\t Символ табуляції ASCII (tab, TAB)
Рис. 1.4
80
Посилання seq може зображувати будь-яку послідовність, та-
ку як список, рядок або кортеж. Значення start, end та step мають
бути цілими числами (або змінними, що зберігають цілі числа).
У першому випадку вилучається елемент послідовності з індек-
сом start. Друга форма запису вилучає підрядок, починаючи з
елемента з індексом start і закінчуючи елементом з індексом end,
не включаючи його. Третю форму запису розглянемо пізніше.
При використанні другої форми запису (з одною двокрап-
кою) ми можемо опустити будь-який з індексів. Якщо опустити
початковий індекс, то за умовчанням буде використовуватися
значення 0. Якщо опустити кінцевий індекс, то за умовчанням
буде використовуватися значення len (seq). Отже, якщо опусти-
ти обидва індекси, наприклад s [:], то це буде рівносильно ви-
слову s [0: len (s)]. У результаті буде залучена (скопійована)
послідовність цілком.
На рис. 1.5 наведено деякі приклади вилучення зрізів з рядка s,
отриманого в результаті присвоювання s = "The waxwork man".
Рис. 1.5
81
Третя форма запису (із двома двокрапками) нагадує другу,
але тут значення step визначає, на якому кроці слід вилучати
символи. Як і при використанні другої форми запису, можна
опустити будь-який з індексів. Якщо опустити початковий ін-
декс, то за умовчанням буде використовуватися значення 0. Як-
що опустити кінцевий індекс, то за умовчанням буде використо-
вуватися значення len (seq). Ми не можемо опустити значення
step і воно не може дорівнювати нулю – якщо задання кроку не
потрібне, то слід використовувати другу форму запису (з одною
двокрапкою), у якій крок вибору елементів не вказується.
На рис. 1.6 наведено два приклади вилучення розріджених
зрізів з рядка s, який отриманий у результаті присвоювання s =
"he ate camel food".
Рис. 1.6
82
Ми вже знаємо, що перевантажена версія оператора "+" для
рядків виконує операцію конкатенації. У випадках, коли необ-
хідно об'єднати безліч рядків, краще використовувати метод
str.join (). Метод приймає як аргумент послідовності й об'єднує
їх у єдиний рядок, вставляючи між ними рядок, для якого був
викликаний метод. Наприклад:
>>> treatises = ["Arithmetica", "Conies", "Elements"]
>>> str.join(treatises)
'Arithmetica Conies Elements'
Метод str.join () може використовуватися в комбінації зі вбу-
дованою функцією reversed (), яка перевертає рядок, наприклад
str.join (reversed (s)). Однак той самий результат можна отрима-
ти коротшим оператором вилучення розрідженого зрізу, напри-
клад s [:: -1].
Оператор "*" забезпечує можливість дублювання рядка:
>>> s = “=” * 5
>>> print(s)
…
>>> s *= 10
>>> print(s)
Як показано в прикладі, ми можемо також використовувати
комбінований оператор присвоювання з дублюванням.
Коли оператор перевірки на входження in застосовується до
рядків, він повертає True, якщо операнд ліворуч є підрядком
операнда праворуч або дорівнює йому. Коли необхідно точно
визначити позицію підрядка в рядку, можна використовувати
два методи. Перший метод str.index () повертає позицію підряд-
ка в рядку або збуджує виключення ValueError, якщо підрядок
не знайдено. Другий метод str.find () повертає позицію підрядка
в рядку або -1 – у випадку невдачі. Обидва методи приймають
шуканий підрядок як перший аргумент і можуть приймати ще
пару необов'язкових аргументів. Другий аргумент визначає по-
зицію початку пошуку, а третій – позицію закінчення пошуку.
Коли ми отримуємо рядки із зовнішніх джерел (з інших про-
грам, з файлів, через мережні з'єднання або в результаті взаємо-
дії з користувачем), вони можуть містити на початку або в кінці
небажані пробільні символи. Видалити пробільні символи, що
83
містяться на початку рядка, можна за допомогою методу
str.lstrip (), у кінці рядка – методу str.rstrip (), а з обох кінців – за
допомогою методу str.strip (). Ми можемо також передавати
методам сім'ї * strip рядки, у цьому випадку вони видалять кож-
не входження кожного символу з відповідного кінця (або з обох
кінців) рядка. Наприклад:
>>> s = "\t no parking "
>>> s.lstrip(), s.rstrip(), s.strip()
('no parking ', *\t no parking', 'no parking')
Можна також заміщати підрядки в рядках, використовуючи
метод str.replace(). Цей метод приймає два рядкові аргументи й
повертає копію рядка, у контексті якого був викликаний метод, де
кожне входження рядка в першому аргументі заміщене рядком у
другому аргументі. Якщо другий аргумент є порожнім рядком, то
видаляються всі входження рядка в першому аргументі.
Часто буває необхідно розбити рядок на список рядків. На-
приклад, у нас може бути текстовий файл з даними, у якому
одному рядку відповідає один запис, а поля всередині запису
відокремлюються одне від одного зірочками. Реалізувати таке
розбиття можна за допомогою методу str.split (), передавши йо-
му рядок, на якому виконується розбиття, у вигляді першого
аргументу, і максимальну кількість розбиттів – у вигляді друго-
го, необов'язкового аргументу. Якщо другий аргумент не вказа-
но, то виконується стільки розбиттів, скільки потрібно. Нижче
наведено приклад використання методу:
>>> record = "Leo Tolstoy*1828-8-28*1910-11-20"
>>> fields = record.split("*")
>>> fields
['Leo Tolstoy', '1828-8-28', '1910-11-20']
Форматування рядків за допомогою методу str.format ().
Метод str.format () є дуже потужним і гнучким засобом створення
рядків. Його використання в простих випадках не викликає труд-
нощів, але для складніших треба знати синтаксис форматування.
Метод str.format () повертає новий рядок, заміщаючи поля в
контекстному рядку відповідними аргументами. Наприклад:
>>> "The novel {0} was published in {1}".format("Hard Times", 1854)
"The novel 'Hard Times' was published in 1854"
84
Кожне заміщене поле ідентифікується ім'ям поля у фігурних
дужках. Якщо як ім'я поля використовується ціле число, то воно
визначає порядковий номер аргументу, переданого методу
str.formate (). Тому в даному випадку поле з ім'ям 0 було замі-
щене першим, а поле з ім'ям 1 – другим аргументом.
1.11.1. Список
21
Інфіксна нотація – це форма запису математичних і логічних фо-
рмул, у якій оператори записані в інфіксному стилі між операндами, на
які вони впливають (наприклад 2 + 2).
86
Список елементів має бути у квадратних дужках, щоб Python
зрозумів, що це список. Як тільки список створений, можна до-
давати, видаляти або шукати елементи в ньому.
Оскільки елементи можна додавати й видаляти, то ми кажемо,
що список – це змінний тип даних, тобто його можна модифікувати.
NameList = [Item1, Item2,…,ItemN]
Короткий вступ до об'єктів і класів. Список – це один із при-
кладів використання об'єктів і класів. Коли ми призначаємо деякій
змінній i значення, припустимо, ціле число 5, це можна зобразити
як створення об'єкта (тобто екземпляра) i класу (тобто типу) int.
Клас може мати методи, тобто функції, визначені для викорис-
тання лише в ньому. Такий функціонал буде доступним, тільки
коли є об'єкт даного класу. Наприклад, Python надає метод append
для класу list, який дозволяє додавати елемент у кінець списку.
Наприклад, mylist.append ('and item') додає рядок до списку mylist.
Зверніть увагу на крапку для доступу до методів об'єктів.
Клас може мати поля (властивості), які є не чим іншим, як
змінними, застосовними тільки в ньому. Ці змінні/імена можна
використовувати лише тоді, коли є об'єкт зазначеного класу.
Доступ до властивостей також здійснюється за допомогою кра-
пки, наприклад: mylist.field.
# Це мій список покупок
shoplist = ['Яблука', 'манго', 'морква', 'банани ']
print('Я повинен зробити ', len(shoplist), 'покупок.')
for item in shoplist:
print('Покупки:', item, end=' ')
print('\n Також потрібно купити рису.')
shoplist.append('рис')
print('Тепер мій список покупок такий :', shoplist)
print('Відсортую-ка я свій список')
shoplist.sort()# у дужках може бути сортувальна функція
print('Відсортований список покупок має такий вигляд:',
shoplist)
print('Перше, що мені потрібно купити, це', shoplist[0])
87
olditem = shoplist[0]
del shoplist[0]
print('Я купив', olditem)
print('Тепер мій список покупок:', shoplist)
Виведення:
Я повинен зробити 4 покупки.
Покупки: яблука манго морква банани
Також потрібно купити рису.
Тепер мій список покупок такий: ['яблука', 'манго', 'морква',
'банани', 'рис']
Відсортую-ка я свій список
Відсортований список покупок має такий вигляд: ['банани',
'манго', 'морква', 'рис', 'яблука']
Перше, що мені потрібно купити, це банани
Я купив банани
Тепер мій список покупок: ['манго', 'морква', 'рис', 'яблука']
Як це працює. Змінна shoplist – це список покупок. У список
можна додавати будь-які об'єкти, включаючи числа або навіть
інші списки.
Ми також використовували цикл for..in для ітерації за елеме-
нтами списку. Ви вже, напевно, зрозуміли, що список є також
послідовністю.
Зверніть увагу на використання ключового аргументу end у
функції print, який показує, що ми хочемо закінчити виведення
пробілом замість звичайного переведення рядка.
Далі додаємо елемент до списку за допомогою append. Потім
перевіряємо, чи дійсно елемент був доданий до списку, виводя-
чи зміст списку на екран за допомогою простої передачі цього
списку функцією print.
Потім сортуємо список, використовуючи метод sort об'єкта
списку. Майте на увазі, що цей метод діє на сам список, а не
повертає його змінену версію.
Далі вказуємо, що хочемо видалити перший елемент списку,
для чого пишемо "del shoplist [0]" (пам'ятаєте, що Python почи-
нає відлік з 0).
88
Псевдоніми та копіювання списків. Розглянемо важливу
особливість списків:
>>> h
['bonjour', 7, 'hola', -1.0, 'привіт']
>>> p=h # містять покажчик на той самий список
>>> p
['bonjour', 7, 'hola', -1.0, 'привіт']
>>> p[0]=1 # модифікуємо одну зі змінних
>>> h # змінилася інша змінна!
[1, 7, 'hola', -1.0, 'привіт']
>>> p
[1, 7, 'hola', -1.0, 'привіт']
>>>
У Python дві змінні називаються псевдонімами22, якщо вони
містять однакові адреси пам'яті.
На рис. 1.7 видно, що змінні p та h указують на той самий список.
Рис. 1.7
22
Псевдоніми – альтернативні імена.
23
За допомогою is порівнюються посилання-адреси, а не самі об'єкти.
89
True
>>> x = [1, 2]
>>> y = [1, 2]
>>> x is y
False
>>>
До списків застосовуються два види копіювання. Перший – по-
верхневе копіювання, за якого створюється новий об'єкт, але він бу-
де заповнений посиланнями на елементи, що містилися в оригіналі:
>>> a = [4, 3, [2, 1]]
>>> b = a[:]
>>> b is a
False
>>> b[2][0]=-100
>>> a
[4, 3, [-100, 1]] # список a також змінився
>>>
Другий вид – глибоке копіювання. За глибокого копіювання
створюються новий об'єкт і рекурсивно – копії всіх об'єктів, що
містяться в оригіналі:
>>> import copy
>>> a = [ 4, 3, [2, 1] ]
>>> b = copy.deepcopy(a)
>>> b[2][0]=-100
>>> a
[4, 3, [2, 1]] # список a не змінився
>>>
1.11.2. Кортеж
90
Кортежі утворюються перелічуванням елементів, розділених
комами; за бажанням їх можна поміщати в круглі дужки.
Кортежі зазвичай використовуються в тих випадках, коли
оператор або призначена для користувача функція мають напев-
но знати, що набір значень, тобто кортеж значень, не зміниться.
zoo = ('пітон', 'слон', 'пінгвін') # пам'ятайте, що дужки не обо-
в'язкові
print('Кількість тварин у зоопарку -', len(zoo))new_zoo = 'мав-
па', 'верблюд', zoo
print('Кількість тварин у зоопарку -', len(new_zoo))
print('Усі тварини в новому зоопарку:', new_zoo)
print('Тварини, що привезені зі старого зоопарку:',
new_zoo[2])
print('Остання тварина, що привезена зі старого зоопарку -',
new_zoo[2][2])
print('Кількість тварин у новому зоопарку -', len(new_zoo)-
1+len(new_zoo[2]))
Виведення:
Кількість тварин у зоопарку – 3
Кількість кліток у зоопарку – 3
Усі тварини в новому зоопарку: (мавпа, 'верблюд', ('пітон',
'слон','пінгвін'))
Тварини, що привезені зі старого зоопарку: ('пітон', 'слон',
'пінгвін')
Остання тварина, що привезена зі старого зоопарку – пінгвін
Кількість тварин у новому зоопарку – 5
Як це працює. Змінна zoo позначає кортеж елементів. Функ-
ція len дозволяє отримати довжину кортежу. Це також указує на
те, що кортеж є послідовністю.
Тепер ми переміщаємо тварин у новий зоопарк, оскільки ста-
рий закривається. Тому кортеж new_zoo містить тварин, які вже
є там, разом із привезеними зі старого зоопарку. Зверніть увагу,
що кортеж усередині кортежу не втрачає індивідуальності.
Доступ до елементів кортежу здійснюється за значенням по-
зиції елемента, поміщеного у квадратні дужки, так само, як ми
робили для списків. Доступ до третього елемента в new_zoo ми
91
отримуємо, указуючи new_zoo[2], а доступ до третього елемента
всередині третього елемента в кортежі new_zoo – указуючи
new_zoo [2] [2]. Це досить просто, якщо розуміти принцип.
Дужки. Хоча дужки не є обов'язковими, усе ж таки краще
завжди вказувати їх, щоб було очевидно, що це кортеж, особли-
во у двозначних випадках. Наприклад, "print (1,2,3)" і "print
((1,2,3))" роблять різні речі: перший вираз виводить три числа,
тоді як другий – кортеж, що містить ці три числа.
Кортеж, що містить 0 або 1 елемент. Порожній кортеж
створюється за допомогою порожньої пари дужок – "myempty =
()". Однак із кортежем з одного елемента не все просто. Його
потрібно вказувати за допомогою коми після першого (і єдино-
го) елемента, щоб Python міг відрізнити кортеж від дужок, що
оточують об'єкт у виразі. Таким чином, щоб отримати кортеж,
який містить елемент 2, треба вказати "singleton = (2,)".
1.11.3. Словник
93
1.11.4. Послідовності
94
Елемент -2 – морква
Символ 0 – s
Елементи з 1 до 3: ['манго', 'морква']
Елементи з 2 до кінця: ['морква', 'банани']
Елементи з 1 до -1: ['манго', 'морква']
Елементи від початку до кінця: ['яблука', 'манго', 'морква',
'банани']
Символи з 1 до 3: wa
Символи з 2 до кінця: aroop
Символи з 1 до -1: waroo
Символи від початку до кінця: swaroop
Як це працює. Перш за все, ми бачимо, як використовувати
індекси для отримання окремих елементів послідовності (це ще
називають приписуванням індексу). Коли ми вказуємо число у
квадратних дужках після послідовності, як показано вище,
Python відображає елемент, що відповідає наведеній позиції в
послідовності. Пам'ятайте, що Python починає відлік з 0. Тому
shoplist [0] відображає перший, а shoplist [3] – четвертий еле-
мент послідовності shoplist.
Індекс також може бути від'ємним числом. У цьому випадку
позиція відраховується від кінця послідовності. Тому shoplist [-
1] указує на останній елемент послідовності shoplist, а shoplist [-
2] – на передостанній.
Операція вирізання виконується за допомогою указування
імені послідовності, за яким може йти пара чисел, розділених
двокрапкою і поміщених у квадратні дужки. Це схоже на опера-
цію індексування, яку ми застосовували досі. Пам'ятайте, що
числа в дужках необов'язкові, тоді як двокрапка – обов'язкова.
Перше число (перед двокрапкою) в операції вирізання вказує
на позицію, з якої зріз має починатися, а друге (після двокрапки)
– де він має закінчитися. Якщо перше число не вказане, то
Python почне вирізання з початку послідовності. Якщо пропу-
щене друге число, то Python закінчить вирізання в кінці послі-
довності. Зверніть увагу, що отримана вирізка буде починатися
зі вказаної початкової позиції, а закінчуватися перед зазначеною
кінцевою позицією, тобто початкова позиція буде включена у
вирізку, а кінцева – ні.
95
Таким чином, shoplist [1: 3] повертає вирізку з послідовності,
що починається з позиції 1, включає позицію 2, але зупиняється
на позиції 3 і тому повертає вирізку з двох елементів. Аналогіч-
но shoplist [:] повертає копію всієї послідовності.
Вирізання може здійснюватися і з від'ємними значеннями.
Від'ємні числа позначають позицію з кінця послідовності. На-
приклад, shoplist [: – 1] поверне вирізку з послідовності, яка ви-
ключає останній елемент, але містить усі інші.
Крім того, можна також указати третій аргумент для вирізання,
що буде позначати крок вирізання, який за умовчанням дорівнює 1:
>>> shoplist = ['яблука', 'манго', 'морква', 'банани']
>>> shoplist[::1]
['яблука', 'манго', 'морква', 'банани']
>>> shoplist[::2]
['яблука', 'морква']
>>> shoplist[::3]
['яблука', 'банани']
>>> shoplist[::-1]
['банани', 'морква', 'манго', 'яблука']
Зверніть увагу: коли крок дорівнює 2, ми отримуємо елемен-
ти, що знаходяться на позиціях 0, 2, ... Коли крок дорівнює 3, ми
отримуємо елементи з позицій 0, 3, ... і т. д.
Послідовності чудові тим, що дають можливість звертатися
до кортежів, списків і рядків одним способом!
1.11.5. Множина
98
>>> list(zip(english, ukraine))
[('Monday', 'Понеділок'), ('Tuesday', 'Вівторок'), ('Wednesday',
'Середа')]
Передайте результат роботи функції zip () безпосередньо фу-
нкції dict () – і отримаєте невеликий англо-український словник:
>>> dict(zip(english, ukraine))
{'Monday': 'Понеділок', 'Tuesday': 'Вівторок', 'Wednesday': 'Середа'}
Генерування числових послідовностей за допомогою фу-
нкції range (). Функція range () повертає потік чисел у заданому
діапазоні без необхідності створювати і зберігати велику струк-
туру даних на зразок списку або кортежу. Це дозволяє створю-
вати великі діапазони, не використавши всю пам'ять комп'ютера
і не "обрушивши" програму:
range(start, stop, step)
Якщо опустити значення start, то діапазон почнеться з 0. Необ-
хідним є лише значення stop: воно визначає останнє значення, яке
буде створене безпосередньо перед зупинкою функції. Значення за
умовчанням step дорівнює 1, але ви можете змінити його на -1.
Як і zip (), функція range () повертає ітераційний об'єкт, тому
необхідно пройти за значеннями за допомогою конструкції for ...
in або перетворити об'єкт на послідовність на зразок списку.
Створимо діапазон 0, 1, 2:
>>> for x in range(0,3):
... print(x)
...
0
1
2
>>> list(range(0, 3))
[0, 1, 2]
Так можна створити діапазон від 2 до 0:
>>> for x in range(2, -1, -1):
... print(x)...
2
1
99
0
>>> list(range(2, -1, -1))
[2, 1, 0]
Включення. Включення – це компактний спосіб створити
структуру даних із одного або більше ітераційних об'єктів.
Включення дозволяють об'єднувати цикли й умовні перевірки,
не використовуючи громіздкий синтаксис. Іншими словами, це
одна з характерних особливостей Python.
Включення списків. Можна створити список цілих чисел від 1
до 5, додаючи їх туди по одному за раз, наприклад:
>>> number_list = []
>>> number_list.append(1)
>>> number_list.append(2)
>>> number_list.append(3)
>>> number_list.append(4)
>>> number_list.append(5)
>>> number_list
[1, 2, 3, 4, 5]
Можна також використовувати ітераційний елемент і функ-
цію range ():
>>> number_list = []
>>> for number in range(1, 6):
... number_list.append(number)
...
>>> number_list
[1, 2, 3, 4, 5]
або перетворити на список результат роботи функції range ():
>>> number_list = list(range(1, 6))
>>> number_list
[1, 2, 3, 4, 5]
Усі ці підходи абсолютно коректні в Python і генерують од-
наковий результат. Однак характернішим для Python є створен-
ня списку за допомогою включення списку. Найпростіша форма
такого включення має вигляд:
[Вираз for елемент in ітераційний об'єкт]
100
Включення списку цілих чисел має такий вигляд:
>>> number_list = [number for number in range(1,6)]
>>> number_list
[1, 2, 3, 4, 5]
У першому рядку потрібно, щоб перша змінна number сфор-
мувала значення для списку: слід розмістити результат роботи
циклу в змінній number_list. Друга змінна number є частиною
циклу for. Щоб показати, що перша змінна number є виразом,
спробуємо такий варіант:
>>> number_list = [number-1 for number in range(1,6)]
>>> number_list
[0, 1, 2, 3, 4]
Включення списку переміщує цикл у квадратні дужки. Роз-
глянутий приклад включення ненабагато простіший за попере-
дній, але це ще не все. Включення списку може містити умов-
ний вираз, який має приблизно такий вигляд:
[Вираз for елемент in ітераційний об'єкт if умова]
Зробимо нове включення, яке створює список, що складаєть-
ся тільки з парних чисел, розташованих у діапазоні від 1 до 5
(пам'ятайте, що вираз number% 2 має значення True для парних
чисел і False – для непарних):
>>>a_list = [number for number in range(1,6) if number %
2 == 1]
>>> a_list
[1, 3, 5]
Тепер включення має трохи компактніший вигляд порівняно
з його традиційним аналогом:
>>> a_list = []
>>> for number in range(1,6):
... if number % 2 == 1:
... a_list.append(number)
...
>>> a_list
[1, 3, 5]
.
101
Нарешті так само, як у разі вкладених циклів, можна написа-
ти більш ніж один набір операторів for... для відповідного виве-
дення даних. Щоб продемонструвати це, спочатку створимо
вкладений цикл і виведемо на екран результат:
>>> rows = range(1,4)
>>> cols = range(1,3)
>>> for row in rows:
... for col in cols:
... print(row, col)
...
11
12
21
22
31
32
Тепер скористаємося включенням і дамо його змінній cells,
створюючи тим самим список кортежів (row, col):
>>> rows = range(1,4)
>>> cols = range(1,3)
>>> cells = [(row, col) for row in rows for col in cols]
>>> for cell in cells:
... print(cell)
...
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
Включення словника. Для словників також можна створити
включення. Найпростіша його форма має звичний вигляд:
{Вираз_ключа: вираз_значення for вираз in ітераційний об'єкт}
Як і у випадку зі включеннями списку, виділення словників
також мають перевірки if і кілька операторів for:
>>> word = 'letters'
102
>>> letter_counts = {letter: word.count(letter) for letter in word}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}
Ми запускаємо цикл, проходячи по кожній із семи літер у ря-
дку letters, і рахуємо, скільки разів з'являється ця літера. Два
наші виклики word.count (letter) – лише марне витрачання часу,
оскільки нам треба підрахувати літери "e" та "t" два рази. Однак
коли ми рахуємо літеру "e" удруге, то не заподіюємо шкоди,
оскільки лише замінюємо вже існуючий запис у словнику; те
саме стосується підрахунку літер "t". Такий спосіб розв'язання
задачі більш характерний для Python:
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in
set(word)}
>>> letter_counts
{'t': 2, 'l': 1, 'e': 2, 'r': 1, 's': 1}
Ключі словника розташовуються в іншому, ніж у попере-
дньому прикладі, порядку, оскільки ітерація по результату робо-
ти функції set (word) повертає літери в іншому порядку, ніж
ітерація по рядку word.
Включення множини. Найпростіша версія має вигляд вклю-
чення списку або словника, які ви щойно бачили:
{Вираз for вираз in ітераційний об'єкт}
Довші версії (перевірки if, множинні оператори for) також
доступні для множин:
>>> a_set = {number for number in range(1,6) if number % 3 == 1}
>>> a_set
{1, 4}
Включення генератора. У Python генератор – це об'єкт, який
призначений для створення послідовностей. Застосування генера-
торів – один зі способів надати дані ітераційному об'єкту. Можна
виконувати ітерацію безпосередньо по цьому об'єкту генератора:
>>> for number in number_thing:
... print(number)
...
103
1
2
3
4
5
Можна також обернути виклик list () навколо включення ге-
нератора, щоб змусити його працювати як включення списку:
>>> number_list = list(number_thing)
>>> number_list
[1, 2, 3, 4, 5]
Вправи
1. Прогресія. Дано ціле число N (> 1), а також перший член A
і різниця D арифметичної прогресії (кількість символів у прі-
звищі та імені студента, відповідно). Сформувати й вивести ма-
сив розміром N, що містить N перших членів такої прогресії:
A, A + D, A + 2 × D, A + 3 × D,. . . .
2. Масив сум попередніх членів. Дано цілі числа N (> 2), A та
B (кількість символів у прізвищі та імені студента, відповідно).
Сформувати й вивести цілочисловий масив розміром N, перший
елемент якого дорівнює A, другий – B, а кожний наступний –
сумі всіх попередніх.
3. Формування масиву із середньоарифметичного елементів
іншого масиву. Дано масив A розміром N. Сформувати новий
масив B того самого розміру за таким правилом: елемент BK
дорівнює середньому арифметичному елементів масиву A з но-
мерами від 1 до K.
4. Об'єднання двох масивів. Дано два масиви A та B розміром
5, елементи яких упорядковані за зростанням. Об'єднати масиви
так, щоб результуючий масив C (розміром 10) залишився впоря-
дкованим за зростанням.
5. Об'єднання трьох масивів. Дано три цілочислові масиви A,
B та C розміром NA, NB, NC, відповідно, елементи яких упоряд-
ковані за спаданням. Об'єднати масиви так, щоб результуючий
цілочисловий масив D (розміром NA + NB + NC) залишився впо-
рядкованим за спаданням.
6. Сортування масиву. Дано масив розміром N. Упорядкувати йо-
го елементи за зростанням. Вбудовані функції не використовувати.
104
1.12. Робота з файлами
Відкривати й використовувати файли для читання або запису
можна шляхом створення об'єкта класу file, а читати/записувати
у файл – за допомогою його методів read, readline або write, від-
повідно. Можливість читати або записувати у файл залежить від
режиму, зазначеного при відкривання файлу. Після закінчення
роботи з файлом треба викликати метод close, щоб указати
Python, що файл більше не використовується.
poem = '''\
Програмувати весело.
Якщо робота нудна,
Щоб надати їй веселий тон –
використовуй Python!
'''
f = open('poem.txt', 'w') # відкриваємо для запису(writing)
f.write(poem) # записуємо текст у файл
f.close() # закриваємо файл
f = open('poem.txt') # якщо не вказано режим, за умовчанням
# мається на увазі режим читання('r'eading)
while True:
line = f.readline()
if len(line) == 0: # Нульова довжина – кінець файлу(EOF)
break
print(line, end='')
f.close() # закриваємо файл
Виведення:
Програмувати весело.
Якщо робота нудна,
щоб надати їй веселий тон –
використовуй Python!
Як це працює. Спочатку ми відкриваємо файл за допомогою
вбудованої функції open із зазначенням імені файлу та режиму,
у якому хочемо його відкрити. Режим може бути для читання
105
('r'), запису ('w') або додавання ('a'). Можна також указати, у яко-
му вигляді ми будемо зчитувати, записувати або додавати дані:
текстовому ('t') або бінарному ('b'). Насправді існують багато ін-
ших режимів, і help (open) дасть їх детальний опис. За умовчан-
ням open () відкриває файл як текст у режимі для читання.
У нашому прикладі ми спочатку відкриваємо файл у режимі за-
пису тексту і використовуємо метод write файлового об'єкта для
запису у файл, після чого закриваємо файл за допомогою close.
Далі відкриваємо той самий файл для читання. У цьому ви-
падку немає потреби вказувати режим, оскільки режим "читання
текстового файлу" застосовується за умовчанням. Ми зчитуємо
файл методом readline у циклі. Цей метод повертає повний ря-
док, включаючи символ переведення рядка в кінці. Коли ж він
повертає порожній рядок, це означає, що ми досягли кінця фай-
лу; тоді перериваємо цикл за допомогою break.
За умовчанням функція print () виводить текст, автоматично
додаючи символ переведення рядка в кінці. Ми ігноруємо цей
символ, указуючи end = '', оскільки рядки й без того закінчують-
ся символом переведення рядка. І, нарешті, закриваємо файл за
допомогою close.
Тепер перевіряємо зміст файлу poem.txt, щоб переконатися,
що програма дійсно записала текст у нього і зчитувала з нього.
24
CSV (Comma Separated Values) – змінні, розділені комами.
106
... ['Mister', 'Big'],
... ['Auric', 'Goldfinger'],
['Ernst', 'Blofeld'],
... ]
>>> with open('villains', 'wt') as fout: # менеджер контексту
... csvout = csv.writer(fout)
... csvout.writerows(villains)
Виведення:
Doctor,No
Rosa,Klebb
Mister,Big
Auric,Goldfinger
Ernst,Blofeld
Конструкція with ... as використовується для гарантії того, що
критичні функції виконаються в будь-якому випадку. Найпоши-
реніший приклад використання цієї конструкції – відкривання
файлів, оскільки вона зручна і гарантує закривання файлу в
будь-якому випадку.
Тепер спробуємо зворотне зчитування:
>>> import csv
>>> with open('villains', 'rt') as fin: # менеджер контексту
... cin = csv.reader(fin)
... villains = [row for row in cin] # використання включення
списку
...
>>> print(villains)
[['Doctor', 'No'], ['Rosa', 'Klebb'], ['Mister', 'Big'],
['Auric', 'Goldfinger'], ['Ernst', 'Blofeld']]
Ми скористалися структурою, породженою функцією reader
(). Вона створила в об'єкті cin рядки, які ми можемо зчитати за
допомогою циклу for.
Використовуючи функції reader () і writer () з їхніми стандар-
тними опціями, отримаємо колонки, розділені комами, і рядки,
розділені символами переведення рядка.
107
Дані можуть мати формат списку словників, а не списку спи-
сків. Знову зчитаємо файл villains, цього разу використовуючи
нову функцію DictReader () і вказуючи імена колонок:
>>> import csv
>>> with open('villains', 'rt') as fin:
... cin = csv.DictReader(fin, fieldnames=['first', 'last'])
... villains = [row for row in cin]
...
>>> print(villains)
[{'last': 'No', 'first': 'Doctor'},
{'last': 'Klebb', 'first': 'Rosa'},
{'last': 'Big', 'first': 'Mister'},
{'last': 'Goldfinger', 'first': 'Auric'},
{'last': 'Blofeld', 'first': 'Ernst'}]
108
Так само просто можна відновити об'єкт (f – файловий об'єкт,
відкритий для читання):
x = pickle.load(f)
Використання модуля pickle є стандартним у мові Python
шляхом збереження тривалих за часом (persistent) об'єктів для
подальшого використання. Модуль настільки часто використо-
вується, що багато авторів додаткових модулів піклуються про
те, щоб нові типи даних (наприклад матриці) могли бути прави-
льно законсервовані.
import pickle
# ім'я файлу, у якому зберігається об'єкт
shoplistfile = 'shoplist.data'
# список покупок
shoplist = ['яблука', 'манго', 'морква']
# Запис у файл
f = open(shoplistfile, 'wb')
pickle.dump(shoplist, f) # розміщуємо об'єкт у файлі
f.close()
del shoplist # знищуємо змінну shoplist
# Зчитуємо зі сховища
f = open(shoplistfile, 'rb')
storedlist = pickle.load(f) # завантажуємо об'єкт із файлу
print(storedlist)
Виведення:
['яблука', 'манго', 'морква']
Як це працює. Щоб зберегти об'єкт у файлі, треба спочатку
відкрити файл за допомогою open у режимі бінарного запису
('wb'), після чого викликати функцію dump із модуля pickle. Цей
процес називається консервацією (pickling).
Потім зчитуємо об'єкт за допомогою функції load з модуля
pickle. Цей процес називається розконсервуванням (unpickling).
Вправи
1. Створення файлу арифметичної прогресії. Дано ім'я файлу і
дійсні числа A та D. Створити файл дійсних чисел з такою назвою і
записати в нього 10 перших членів арифметичної прогресії з почат-
ковим членом A та різницею D: A, A + D, A + 2 × D, A + 3 × D,. . . .
109
2. Створення файлу на основі даних іншого файлу. Дано
файл цілих чисел. Створити новий файл, який містить ті самі
елементи, що й вихідний файл, але у зворотному порядку.
3. Перетворення елементів файлу цілих чисел. Дано файл ці-
лих чисел з елементами A1, A2,. . ., AN (N – кількість елементів у
файлі). Замінити вихідне розташування його елементів на таке:
A1, AN, A2, AN-1, A3,. . . .
4. Заміна змісту файлів. Дано два файли довільного типу.
Поміняти місцями їхні змісти.
5. Створення файлу з даних інших файлів. Дано три файли
цілих чисел однакового розміру з іменами SA, SB, СК і рядок SD.
Створити новий файл з ім'ям SD, у якому чергувалися б елемен-
ти вихідних файлів з тим самим номером:
A1, B1, C1, A2, B2, C2,. . . .
6. Створення файлу транспонованої матриці. Дано файл дійс-
них чисел, що містить елементи квадратної матриці (по рядках).
Створити новий файл, який містить елементи матриці, транспо-
нованої до вихідної.
7. Створення файлу суми матриць. Дано два файли дійсних
чисел з іменами SA та SB, що містять елементи квадратних мат-
риць A та B (по рядках). Створити нові файли: з ім'ям SC1, що
містить елементи суми A + B; SC2, що містить елементи різниці
A – B; SC3, що містить елементи добутку A × B.
1.13.2. Виняток
112
• Якщо під час виконання гілки try генерується виняток, то
решта гілки пропускається. Далі, якщо тип (клас) винятку відпо-
відає зазначеному після ключового слова except, то виконується
гілка except і виконання інструкції try завершується.
• Якщо виняток не відповідає зазначеному після ключового слова
except типу помилки, то він передається зовнішньому блоку try або,
якщо обробник не знайдений, вважається неперехопленим. Тоді
виконання переривається і виводиться повідомлення про помилку.
Інструкція try може мати більше однієї гілки except, визна-
чаючи обробники для різних винятків. Виконуватися буде тіль-
ки один з них. Обробляються тільки винятки, що генеровані у
відповідній гілці try, але не в інших обробниках інструкції try.
Після ключового слова except може бути зазначено кілька типів
винятків у вигляді кортежу:
... except (RuntimeError, TypeError, NameError):
... pass
Після всіх гілок except інструкція try може містити гілку else,
яка буде виконуватися у випадку, якщо під час виконання гілки
try винятки не генеруються.
Виняток може мати асоційоване з ним значення – аргумент,
переданий класу винятків при ініціалізації. Наявність і тип ар-
гументу залежать від типу винятку. Асоційоване значення вико-
ристовується при отриманні для винятку рядкового значення.
Щоб отримати значення винятку, у гілці except після класу ви-
нятків (або кортежу класів) укажіть змінну:
>>> try:
... spam()
... except NameError, x:
... print “Ім'я”, x, “не визначено”
...
Ім'я spam не визначене
Якщо виняток не обробляється, то його значення виводиться
в повідомленні про помилку після імені класу винятків.
Оброблювач перехоплює не тільки винятки, що згенеровані
безпосередньо в блоці try, а й ті, що згенеровані у функціях, які
з нього викликаються. Наприклад:
>>> def this_fails():
... x = 1/0
113
...
>>> try:
... this_fails()
... except ZeroDivisionError, exc:
... print ('Помилка часу виконання:', exc)
...
Помилка часу виконання: integer division or modul
1.14. Визначення
та використання класів
Два основні аспекти об'єктно-орієнтованого програмування –
класи та об'єкти. Клас створює новий тип, а об'єкти є екземпля-
рами класу. Коли ми говоримо про змінні типу int, це означає,
що змінні, які зберігають цілочислові значення, є екземплярами
(об'єктами) класу int.
Об'єкти можуть зберігати дані у звичайних змінних, що
належать об'єкту. Змінні, що належать об'єкту або класу, на-
зиваються полями. Об'єкти також можуть мати деякий функ-
ціонал, тобто функції, що належать класу. Такі функції при-
йнято називати методами класу. Ця термінологія важлива,
оскільки допомагає відрізняти незалежні функції та змінні від
тих, що належать класу або об'єкту. Усе разом (поля і методи)
називають атрибутами класу.
Поля бувають двох типів: вони можуть належати кожному
окремому екземпляру об'єкта класу або всьому класу і нази-
ваються змінними екземпляра або змінними класу, відповідно.
Клас створюється ключовим словом class. Поля і методи кла-
су записуються у блоці коду з відступом.
117
1.14.1. Класи
119
1.14.4. Змінні класу та об'єкта
121
Як це працює. Наведений приклад допомагає продемонстру-
вати природу змінних класу і об'єкта. Тут population належить
класу Robot, тому є змінною класу. Змінна name належить об'-
єкту (їй присвоюється значення за допомогою self), тому є
змінною об'єкта.
Таким чином, ми звертаємося до змінної класу population як
Robot.population, а не self.population. До змінної ж об'єкта name
у всіх методах цього об'єкта ми звертаємося за допомогою по-
значення self.name. Пам'ятайте про цю просту різницю між
змінними класу та об'єкта. Також майте на увазі, що змінна
об'єкта з тим самим ім'ям, що і змінна класу, зробить останню
недоступною ("сховає").
Метод howMany належить класу, а не об'єкту. Це означає, що
можна визначити його як classmethod або staticmethod, залежно
від того, чи треба нам знати, з яким класом ми працюємо. Оскі-
льки нам не потрібна така інформація, то скористаємося
staticmethod.
Можна досягти того самого результату, використовуючи де-
коратори:
@staticmethod
def howMany():
'''Виводить кількість роботів.'''
print('У нас {0:d} роботів.'.format(Robot.population))
Декоратори можна вважати певним спрощеним способом ви-
клику явного оператора, як ми бачили в цьому прикладі.
Поспостерігайте, як метод __init__ використовується для іні-
ціалізації екземпляра з ім'ям Robot. У цьому методі ми збільшу-
ємо лічильник population на 1, оскільки додаємо ще одного ро-
бота. Зауважте, що значення self.name для кожного об'єкта свої,
що вказує на природу змінних об'єкта.
Пам'ятайте, що до змінних і методів об'єкта треба звертатися,
користуючись тільки self. Це називається доступом до атрибутів.
У наведеному вище прикладі ми спостерігали застосування
рядків документації для класів так само, як для методів. Під час
виконання програми можна звертатися до рядка документації
класу за допомогою Robot.__doc__, а до рядка документації ме-
тоду – за допомогою Robot.sayHi.__doc__.
122
Поряд з методом __init__ існує інший спеціальний метод
__del__, який викликається тоді, коли об'єкт "зникає", тобто
більше не використовується, а зайнята ним пам'ять повертається
операційній системі для інших застосувань. У цьому методі ми
просто зменшуємо лічильник Robot.population на 1.
Заздалегідь невідомо, коли саме "зникне" об'єкт, отже, щоб
побачити явно дію методу __del__, треба скористатися операто-
ром del, що ми і зробили вище.
Примітка для програмістів мовами C++ / Java / C #. У Python
усі члени класу (включаючи дані) є публічними (public), а всі
методи – віртуальними (virtual).
Виняток. Якщо ім'я змінної починається з подвійного підкре-
слення, наприклад __privatevar, то Python робить цю змінну
приватною (private). Тому прийнято ім'я будь-якої змінної, яка
має використовуватися тільки всередині класу або об'єкта, по-
чинати з підкреслення; усі ж інші імена є публічними і можуть
використовуватися в інших класах/об'єктах. Пам'ятайте, що це
лише традиція, і Python зовсім не зобов'язує робити саме так
(крім подвійного підкреслення).
1.14.5. Спадкування
123
Краще створити загальний клас з ім'ям SchoolMember, а потім
зробити так, щоб класи викладачів і студентів його успадковува-
ли, тобто щоб вони стали підтипами цього типу (класу), після
чого додати будь-які специфічні характеристики до цих підтипів.
У такого підходу є безліч переваг. Якщо ми додає-
мо/змінюємо якусь функціональність у SchoolMember, то це
автоматично відображується в усіх підтипах.
Наприклад, ми можемо ввести нове поле Посвідчення для
викладачів і студентів, просто додавши його до класу
SchoolMember. З іншого боку, зміни в підтипах ніяк не вплива-
ють на інші підтипи. Ще одна перевага полягає в тому, що звер-
татися до об'єкта Викладач або Студент можна як до об'єкта
SchoolMember, що є корисним у деяких випадках, наприклад
для підрахунку кількості осіб у школі. Ситуація, коли підтип
може бути підставлений у будь-яке місце, де очікується батьків-
ський тип, тобто об'єкт вважається екземпляром батьківського
класу, називається поліморфізмом.
Код батьківського класу використовується багаторазово, то-
му немає необхідності копіювати його в усі класи, як довелося б
у разі використання незалежних класів.
Клас SchoolMember в описаній ситуації називають базовим
класом, або надкласом. Класи Teacher і Student називають похід-
ними класами, або підкласами.
Розглянемо тепер цей приклад у вигляді програми.
class SchoolMember:
''' Зображує будь-яку людину в школі.'''
def __init__(self, name, age):
self.name = name
self.age = age
print('(Створено SchoolMember: {0})'.format(self.name))
def tell(self): '''Вивести інформацію.'''
print('Ім'я:"{0}" Вік:"{1}"'.format(self.name, self.age),
end=" ")
class Teacher(SchoolMember):
'''Зображує викладача.'''
def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age)
124
self.salary = salary
print('(Створено Teacher: {0})'.format(self.name))
def tell(self):
SchoolMember.tell(self)
print('Заробітна платня: "{0:d}"'.format(self.salary))
class Student(SchoolMember):
'''Зображує студента.'''
def __init__(self, name, age, marks):
SchoolMember.__init__(self, name, age)
self.marks = marks
print('(Створено Student: {0})'.format(self.name))
def tell(self):
SchoolMember.tell(self)
print('Оцінки: "{0:d}"'.format(self.marks))
t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)
print() # друкує порожній рядок
members = [t, s]
for member in members:
member.tell() # працює як для викладача, так і для студента
Виведення:
(Створено SchoolMember: Mrs. Shrividya)
(Створено Teacher: Mrs. Shrividya)
(Створено SchoolMember: Swaroop)
(Створено Student: Swaroop)
Ім'я:"Mrs. Shrividya" Вік:"40" Заробітна платня: "30000"
Ім'я:"Swaroop" Вік:"25" Оцінки: "75"
Як це працює. Щоб скористатися спадкуванням, при визна-
ченні класу треба вказати імена його базових класів у вигляді
кортежу, що розміщується відразу за його ім'ям. Далі ми бачи-
мо, що метод __init__ базового класу викликається явно за до-
помогою змінної self, щоб форматувати частину об'єкта, який
належить до базового класу. Запам'ятайте: Python не викликає
конструктор базового класу автоматично – його необхідно ви-
кликати самостійно у явному вигляді.
125
Ми також бачимо, як можна викликати методи базового кла-
су, випереджаючи запис імені методу ім'ям класу, а потім пере-
даючи змінну self разом з іншими аргументами.
Зверніть увагу, що при виклику методу tell із класу
SchoolMember екземпляри Teacher або Student можна викорис-
товувати як екземпляри SchoolMember.
Зауважте також, що викликається метод tell із підкласу, а не
метод tell із класу SchoolMember. Це можна розуміти так: Python
завжди починає пошук методів у самому класі, що й робить у
даному випадку. Якщо ж він не знаходить метод, то починає
шукати методи, які належать базовим класам по черзі, у поряд-
ку, у якому вони перераховані в кортежі при визначенні класу.
Якщо при спадкуванні перераховано більше одного класу, то
це називається множинним спадкуванням.
Параметр end використовується в методі tell () для того, щоб
новий рядок починався через пробіл після виклику print ().
1.14.6. Метакласи
128
4.2. Нехай Animal буде батьківським для класу Cat. Метод
makeNoise () класу Cat виводить "(Name) говорить Мяу". Конс-
труктор класу Cat виводить "Народився кіт", а також викликає
батьківський конструктор.
4.3. Нехай Animal буде батьківським для класу Dog. Метод
makeNoise () для Dog виводить "(Name) говорить Гав". Констру-
ктор Dog виводить "Народився собака", а також викликає бать-
ківський конструктор.
4.4. Основна програма. Код, що створює кота, двох собак і од-
ну просто тварину. Дайте ім'я кожній тварині (через виклик мето-
дів). Код, що викликає eat () і makeNoise () для кожної тварини.
25
Мова програмування, так само як і будь-яка природна мова, має:
• алфавіт – символи, які можна використовувати в програмах;
• лексику – ключові слова, ідентифікатори, оператори;
• пунктуацію – знаки (; ,. :) і дужки () {} [];
• синтаксис – вирази, конструкції;
• семантику – значення елементів мови для програміста і для ком-
п'ютера.
129
1.15.1. Проект № 1. Гра "Шибениця"
Старт
Задумуємо слово
Показуємо гравцю
шибеницю і пропуски
Просимо гравця
вгадати літеру
Так Ні
Наявність літери
у слові
Так
Відповідний пропуск Чоловічка
заповнюється літерою нарисовано
Ні Ні
Слово Рисується наступний
відгадано елемент чоловічка
Так
Ще гра
Ні Так
Кінець
131
Кінець
Старт Старт
Задумуємо слово
Показуємо гравцю
Кінець шибеницю і пропуски
Просимо гравця
вгадати літеру
Кінець
Старт
Просимо гравця
Задумуємо слово вгадати літеру
Кінець
132
Якщо літера є у згаданому слові, то слід перевірити: можли-
во, гравець угадав усі літери та виграв. Якщо літери у слові не-
має, то треба перевірити: можливо, повішений чоловічок нари-
сований повністю і гравець програв. Додайте поля для цих дій.
Блок-схема тепер виглядає так, як показано на схемі 1.5.
Наступна спроба. Тепер блок-схема здебільшого завершена, але
нам усе ще дечого бракує. По-перше, гравець не вгадує літеру тіль-
ки один раз; він продовжує вгадувати літери доти, поки не виграє
або програє. Дорисуйте дві нові стрілки, як показано на схемі 1.6.
Проміжні результати гри. Гравцю необхідно знати проміж-
ні результати гри. Програма має відобразити рисунок повішено-
го чоловічка і секретне слово з пробілами замість літер, які гра-
вець ще не вгадав. Цей інтерфейс дозволить гравцю зрозуміти,
наскільки він близький до перемоги або програшу.
Старт
Задумуємо слово
Показуємо гравцю
шибеницю і пропуски
Просимо гравця
вгадати літеру
Так Ні
Наявність літери
у слові
Відповідний пропуск Чоловічка
заповнюється літерою намальовано
Ні Ні
Рисується наступний
Слово відгадано
елемент чоловічка
Кінець
133
Інформація оновлюється кожного разу, коли гравець на-
магається вгадати літеру. Додайте блок "Показуємо гравцеві
шибеницю і пробіли" у схему між блоками "Задумується
секретне слово" і "Просимо гравця вгадати літеру ", як пока-
зано на схемі 1.6.
Закінчуємо або починаємо гру спочатку. Коли гравець ви-
грав або програв, запитуємо його, чи хоче він зіграти
заново, щоб відгадати інше слово. Якщо гравець не хоче грати,
то програма завершує роботу; в іншому випадку програма про-
довжує виконання і загадує нове слово (див. схему 1.7).
Старт
Задумуємо слово
Показуємо гравцю
шибеницю і пропуски
Просимо гравця
вгадати літеру
Так Ні
Наявність літери у слові
Відповідний пропуск
Чоловічка нарисовано
заповнюється літерою
Ні
Ні Рисується наступний
Слово відгадано елемент чоловічка
Кінець
134
Старт
Задумуємо слово
Показуємо гравцю
шибеницю і пропуски
Просимо гравця
вгадати літеру
Так Ні
Наявність літери у слові
Ні
Кінець
135
сячи зміни. Набагато ефективніше знати, що ви хочете побуду-
вати в підсумку, перш ніж приступати до будівництва. Тепер,
коли у нас є блок-схема, давайте створимо гру "Шибениця".
Оригінальний код гри "Шибениця". Велика частина
коду цієї гри припадає на ASCII-код для прорисовування пові-
шеного чоловічка.
Введіть наведений нижче вихідний код і збережіть файл під
ім'ям hangman.py. Потім запустіть програму.
1. import random
2. HANGMAN_PICS = ['''
3. +–-+
4. |
5. |
6. |
7. ===''', '''
8. +–-+
9. |
10. |
11. |
12. ===''', '''
13. +–-+
14. 0 |
15. | |
16. |
17. ===''', '''
18. +–-+
19. 0 |
20. /| |
21. |
22. ===''', '''
23. +–-+
24. 0 |
25. /|\ |
26. |
27. ===''', '''
28. +–-+
29. 0 |
136
30. /|\ |
31. / |
32. ===''', '''
33. +–-+
34. 0 |
35. /|\ |
36. / \ |37. ===''']
38. words = 'лелека акула бабуїн баран борсук бобер бик вер-
блюд вовк горобець ворон видра голуб гусак жаба зебра змія
індик кит кобра коза козел койот корова кішка кролик щур кур-
ка лама ласка лебідь лев лисиця лосось лось жаба ведмідь мо-
люск моль мул мурашка миша норка носоріг мавпа вівця окунь
олень орел віслюк панда павук Python папуга пума сьомга скунс
собака сова тигр тритон тюлень качка форель тхір черепаха яст-
руб ящірка '.split()
39.
40. def getRandomWord(wordList):
41. # Ця функція повертає випадковий рядок з переданого
списку
42. wordIndex = random.randint(0, len(wordList) – 1)
43. return wordList[wordIndex]
44.
45. def displayBoard(missedLetters, correctLetters, secretWord):
46. print(HANGMAN_PICS[len(missedLetters)])
47. print()
48.
49. print('Помилкові літери:', end=' ')
50. for letter in missedLetters:
51. print(letter, end=' ')
52. print()
53.
54. blanks = '_' * len(secretWord)
55.
56. for i in range(len(secretWord)):#замінює пробіли відга-
даними
літерами
57. if secretWord[i] in correctLetters:
137
58. blanks = blanks[:i] + secretWord[i] + blanks[i+1:]
59.
60. for letter in blanks: # Показує секретне слово з пробіла-
ми між
літерами
61. print(letter, end=' ')
62. print()
63.
64. def getGuess(alreadyGuessed):
65. # Повертає літеру, введену гравцем. Ця функція переві-
ряє, що гравець увів тільки одну літеру і нічого більше
66. while True:
67. print('Введіть літеру.')
68. guess = input()
69. guess = guess.lower()
70. if len(guess) != 1:
71. print('Будь ласка, введіть одну літеру.')
72. elif guess in alreadyGuessed:
73. print('Ви вже називали цю літеру. Назвіть іншу.')
74. elif guess not in 'абвгґдеєжзиіїйклмнопрстуфхц-
чшщьюя':
75. print('Будь ласка, введіть ЛІТЕРУ.')
76. else:
77. return guess
78.
79. def playAgain():
80. #Ця функція повертає значення True, якщо гравець хо-
че зіграти знову; в іншому випадку повертає False.
81. print('Бажаєте зіграти ще? (так чи ні)')
82. return input().lower().startswith('т')
83.
84.
85. print('Ш И Б Е Н И Ц Я')
86. missedLetters = ''
87. correctLetters = ''
88. secretWord = getRandomWord(words)
89. gameIsDone = False
138
90.
91. while True:
92. displayBoard(missedLetters, correctLetters, secretWord)
93.
94. # Дозволяє гравцеві ввести літеру.
95. guess = getGuess(missedLetters + correctLetters)
96.
97. if guess in secretWord:
98. correctLetters = correctLetters + guess
99.
100. # Перевіряє, чи виграв гравець.
101. foundAllLetters = True
102. for i in range(len(secretWord)):
103. if secretWord[i] not in correctLetters:
104. foundAllLetters = False
105. break
106. if foundAllLetters:
107. print('ТАК! Секретне слово – "' + secretWord + '"!
Ви вгадали!')
108. gameIsDone = True
109. else:
110. missedLetters = missedLetters + guess
111.
112. # Перевіряє, чи перевищив гравець ліміт спроб і
програв.
113. if len(missedLetters) == len(HANGMAN_PICS) – 1:
114. displayBoard(missedLetters, correctLetters, secretWord)
115. print(' Ви вичерпали всі спроби!\n Не угадано лі-
тер: ' + str(len(missedLetters)) + ' і угадано літер: ' +
str(len(correctLetters)) + '. Було загадано слово "' + secretWord + '".')
116. gameIsDone = True
117.
118. # Запитує, чи хоче гравець зіграти знову (тільки якщо
гра завершена).
119. if gameIsDone:
120. if playAgain():
121. missedLetters = ''
139
122. correctLetters = ''
123. gameIsDone = False
124. secretWord = getRandomWord(words)
125. else:
126. break
Імпортування модуля random. Програма "Шибениця" ви-
падковим чином вибирає слово із заздалегідь визначеного спис-
ку і пропонує вгадати його гравцеві. Можливість випадкового
вибору забезпечує модуль random, який імпортується в першому
рядку коду.
1. import random
Змінна HANGMAN_PICS у другому рядку трохи відрізняєть-
ся від змінних, з якими ви вже знайомі. Для розуміння наступ-
ного коду ви повинні познайомитися з кількома додатковими
концепціями.
Константи. Рядки з 2 по 37 – це одна довга інструкція при-
своювання для змінної HANGMAN_PICS.
2. HANGMAN_PICS = ['''
3. +–-+
4. |
5. |
6. |
7. ===''', '''
–пропущене–
37. ===''']
Існують неформальні угоди запису імен, наприклад: якщо
ім'я змінної складається із великих літер, то за умовчанням це є
константа.
Константи – це змінні, значення яких не змінюються в проце-
сі роботи програми з моменту їх оголошення. Теоретично ви
можете змінити значення змінної HANGMAN_PICS так само
просто, як і будь-якої іншої змінної, тому ім'я великими літера-
ми нагадує вам, щоб ви цього не робили.
Як і у випадку з усіма іншими угодами з оформлення коду,
ви зовсім не зобов'язані їх дотримуватися. Однак, дотримуючись
їх, ви зробите код зрозумілішим для інших програмістів.
140
Списки. Значення змінної HANGMAN_PICS складається із
кількох рядків. Це називається списком. У списках як елементи
можуть міститися різні значення.
Список значень починається і закінчується квадратними ду-
жками – []. Рядкові значення (слова) поміщаються в одинарні
лапки – '' і розділяються комами.
Ці слова називаються елементами списку. Кожен елемент
HANGMAN_PICS – це багаторядкове значення.
Списки дозволяють зберігати кілька значень, не призначаючи
окрему змінну для кожного з них.
Доступ до елементів за їх індексами. Можна отримати до-
ступ до будь-якого елемента списку, указавши його номер у
квадратних дужках у кінці імені змінної. Номер у квадратних
дужках називається індексом елемента. У Python першому еле-
менту присвоюється індекс 0. Другий елемент має індекс 1, тре-
тій – 2 і т. д. Оскільки індекси в списках починаються з 0, а не з
1, то їх називають списками нульового індексу.
Індекс за межами діапазону і помилка IndexError. Якщо
вказати індекс, значення якого перевищує кількість записів у
списку, то виконання програми буде перервано і з'явиться по-
відомлення про помилку IndexError.
Конкатенація списків. Можна з'єднати кілька списків в
один, як і звичайні рядки, використовуючи оператор +.
Оператор in. Оператор in дозволяє визначити наявність у
списку елемента з певним значенням. Вирази з оператором in
повертають значення True, якщо шукане значення міститься у
списку, або False, якщо його там немає.
Оператор in також застосовується до рядків. У цьому випад-
ку він перевіряє, чи є один рядок частиною іншого.
Виклик методів. Метод – це функція, прив'язана до об'єкта (спи-
ску або рядка). Для виклику методу необхідно прив'язати його до
конкретного об'єкта. У мові Python вбудовано багато корисних ме-
тодів, деякі з них ми використовуємо у грі "Шибениця". Однак спо-
чатку розглянемо кілька методів для роботи зі списками та рядками.
Методи списків reverse () та append (). Списки підтримують
два вбудовані методи, які використовуються найчастіше, – reverse
() та append (). Метод reverse () розгортає (звертає) список.
Однак найчастіше у списках використовують метод append ().
Він додає нові значення в кінець списку.
141
Зазначені методи змінюють списки, які їх викликають, і не
створюють нові списки. Це називається зміною списків за місцем.
Рядковий метод split (). Рядки мають вбудований метод split
(), який повертає список, сформований з рядкових змінних, на
які розбивається рядок. У 38-му рядку коду гри "Шибениця" теж
використовується метод split (), як показано нижче. Код здається
довгим, але насправді це просто довгий рядок слів, розділених
пробілами, з викликом методу split () у кінці.
Метод split () створює список, у якому кожне слово з рядка
стає його окремим елементом.
38. words = 'лелека акула бабуїн баран борсук бобер бик верблюд
вовк горобець ворон видра голуб гусак жаба зебра змія індик кит
кобра коза козел койот корова кішка кролик щур курка лама ласка
лебідь лев лисиця лосось лось жаба ведмідь молюск моль мул му-
рашка миша норка носоріг мавпа вівця окунь олень орел віслюк
панда павук Python папуга пума сьомга скунс собака сова тигр три-
тон тюлень качка форель тхір черепаха яструб ящірка'.split ()
Метод split () полегшує програмування. Дійсно, щоб створи-
ти список, треба записати слова в одинарних лапках через кому
й помістити їх у квадратні дужки.
Ви можете додати власні слова до коду рядка 38 або видали-
ти які-небудь із них, якщо не хочете, щоб вони були у грі. Голо-
вне, переконайтеся, що слова розділені пробілами.
Отримання секретного слова зі списку. У рядку 40 визна-
чається функція getRandomWord (). Значення елементів списку
передаються аргументу wordlist як параметри. Ця функція пове-
ртає одиничне секретне слово зі списку.
40. def getRandomWord (wordList):
41. # Ця функція повертає випадковий рядок з переданого списку.
42. wordIndex = random.randint (0, len (wordList) – 1)
43. return wordList [wordIndex]
У рядку 42 як значення змінної wordIndex зберігається елемент
із випадковим індексом. Випадковий вибір елемента виконується
функцією randint () із двома аргументами. Перший аргумент – це
0 (для першого можливого індексу), а другий – значення виразу
len (wordList) – 1, що визначає останній можливий індекс.
142
Нагадаємо, що індексація елементів списку починається з 0, а
не з 1. Якщо є список із трьох елементів, то індекс першого еле-
мента – 0, другого – 1, а третього – 2. Довжина такого списку
дорівнює 3, але індексу 3 у списку немає. Тому в рядку 42 з до-
вжини списку віднімається одиниця. Код у рядку 42 працює
незалежно від розмірів списку wordList. Тепер ви можете спо-
кійно додавати й видаляти рядки зі списку wordList.
Змінна wordIndex зберігає випадковий індекс зі списку, пере-
даного за допомогою параметра wordList. Код у рядку 43 повер-
тає зі списку wordList значення елемента з відповідним індек-
сом, який зберігається як ціле число у wordIndex.
Припустимо, що ['яблуко', 'апельсин', 'виноград'] передається як
аргумент у функцію getRandomWord (), тоді randint (0, 2) повертає 2.
Це означає, що рядок 43, у якому повертається значення wordList [2],
поверне значення 'виноград'. Таким чином функція getRandomWord
() повертає випадковий рядок зі списку wordList, тобто вхідними
даними для функції getRandomWord () є список рядків, а вихідними
– випадково обраний рядок. У грі "Шибениця" таким чином вибира-
ється секретне слово, яке буде вгадувати гравець.
Відображення ігрового поля для гравця. Далі необхідна
функція прорисовування ігрового поля гри "Шибениця". На
ньому має відображатися кількість уведених гравцем літер, уга-
даних як правильно, так і помилково.
45. def displayBoard (missedLetters, correctLetters, secretWord):
46. print (HANGMAN_PICS [len (missedLetters)])
47. print ()
У наступному коді визначається функція з ім'ям displayBoard
(), яка має три параметри:
• missedLetters – рядок літер, які гравець назвав, але їх немає
в задуманому слові;
• correctLetters – рядок літер, які гравець угадав у задуманому
слові;
• secretWord – рядок із задуманим словом, яке гравець нама-
гається вгадати.
Спочатку функція print () викликає відображення ігрового
поля. Глобальна змінна HANGMAN_PICS містить список ряд-
кових змінних для прорисовування всіх можливих етапів гри
(нагадаємо, що глобальні змінні доступні з функції). Код
143
HANGMAN_PICS [0] відображає порожню шибеницю, код
HANGMAN_PICS [1] показує голову (коли гравець назвав не-
правильно одну літеру), код HANGMAN_PICS [2] – голову і
тіло (коли гравець неправильно назвав дві літери) і так далі до
HANGMAN_PICS [6], що показує шибеника цілком.
Кількість літер, що зберігається у змінній missedLetters, відо-
бражає кількість неправильних припущень, зроблених гравцем.
Для визначення цієї кількості викликається функція len
(missedLetters). Отже, якщо значення змінної missedLetters одне,
наприклад 'лелека', то код len ('лелека') поверне 4. Код
HANGMAN_PICS [4] відобразить повішеного, що відповідає
чотирьом помилкам. Це те, на що перетвориться код
HANGMAN_PICS [len (missedLetters)] у рядку 46.
Код у рядку 49 виводить повідомлення 'Помилкові літери:' із
символом пробілу в кінці замість символу нового рядка.
49. print ('Помилкові літери:', end = '')
50. for letter in missedLetters:
51. print (letter, end = '')
52. print ()
У рядку 50 починається цикл for, у якому відбувається пере-
бирання всіх символів із рядкового значення змінної
missedLetters і виведення їх на екран.
Нагадаємо, що вираз end = '' заміщає символ нового рядка,
який поміщається в кінці кожного рядка, одиничним пробілом.
Наприклад, якщо значенням missedLetters є 'аизх', то такий цикл
for виведе на екран а и з х.
Інша частина коду функції displayBoard () (рядки 54–62) ви-
водить на екран літери й формує рядок – секретне слово, у яко-
му ще не вгадані літери, заміщені пробілами. Це досягається за
допомогою функції range () і зрізу списку.
Функції list () та range (). Функція range (), що викликається
з одним аргументом, повертає послідовність чисел від 0 до ве-
личини аргументу; сам аргумент у послідовність не включаєть-
ся. Таку послідовність можна використовувати в циклі for або
перетворити на список за допомогою функції list ().
Функція list () дуже схожа на функції str () або int (). Вона при-
ймає послідовність величин і повертає їх у вигляді списку. За допо-
могою функцій list () і range () легко генерувати величезні списки.
144
Якщо функція range () викликається із двома цілочисловими
аргументами, то вона повертає послідовність чисел, починаючи
з першого аргументу до (не включаючи) другого.
Зрізи списків і рядків. Зріз списку створює новий список з
підмножиною елементів батьківського списку. Для створення
зрізу списку використовуються два індекси (початковий і кінце-
вий), які поміщаються в кінець імені у квадратних дужках і роз-
діляються двокрапкою.
Виведення секретного слова з пробілами. За бажання мож-
на вивести секретне слово з пробілами замість невгаданих літер.
Можна також використовувати символ нижнього підкреслення
(_). Спочатку треба створити рядок із символів нижнього під-
креслення для всіх літер секретного слова, потім замінити про-
біли цими символами.
Наприклад, для секретного слова 'ворон' порожній рядок-
підкреслення має такий вигляд: '_ _ _ _ _' (п'ять підкреслень).
Якщо значення змінної correctLeters одне – 'ворон', то секретне
слово треба надрукувати у вигляді '_ о _ о н'. Код у рядках 54–58
робить саме це.
54. blanks = '_' * len (secretWord)
55.
56. for i in range (len (secretWord)): # замінює пробіли відга-
даними літерами
57. if secretWord [i] in correctLetters:
58. blanks = blanks [: i] + secretWord [i] + blanks [i + 1:]
У рядку 54 створюється рядкова змінна blanks із підкресленнями
за кількістю літер секретного слова. Нагадаємо, що оператор * засто-
совується для змінних рядкового і цілого типів, тому вираз '_' * 5
буде перетворено на рядок '_ _ _ _ _'. Ця операція гарантує, що змінна
blanks містить стільки підкреслень, скільки літер у секретному слові.
Код у рядку 56 у циклі for здійснює послідовне перебирання
всіх літер секретного слова і заміщає підкреслення літерами, що
містяться у змінній correctLetters.
Розглянемо цикл із попереднього прикладу з іншими дани-
ми. Нехай секретне слово 'ворон', а значення змінної
correcLetters одне – 'он'. Треба вивести на екран рядок '_ о _ о
н'. Подумаємо, як це зробити.
145
Код у рядку 56 після виклику len (secretWord) поверне зна-
чення 5. Виклик range (len (secretWord)) набуде вигляду range
(5), тоді цикл for зробить ітерації від 0 до 4 включно.
Оскільки змінна i буде послідовно набувати значень [0, 1, 2,
3, 4], то код циклу for матиме приблизно такий вигляд:
if secretWord[0] in correctLetters:
blanks = blanks[:0] + secretWord[0] + blanks[1:]
if secretWord[1] in correctLetters:
blanks = blanks[:1] + secretWord[1] + blanks[2:]
–пробіл–
Ми показали тільки дві перші ітерації циклу for, але насправ-
ді змінна ітерації i по черзі набуватиме всіх значень послідовно-
сті, починаючи з 0.
У першій ітерації i дорівнюватиме 0, тому оператор if переві-
рить, чи міститься літера секретного слова з індексом 0 у рядку
correctLetters. Ця перевірка здійснюватиметься в циклі для кож-
ної літери секретного слова, по одній ітерації на кожну літеру.
Якщо ви сумніваєтеся стосовно будь-якого значення, напри-
клад secretWord [0] або blank [3:], то зверніться до рис. 1.8, де
наведені значення secretWord, змінної blanks та індекси для ко-
жної літери в рядку.
_ _ _ _ _
blanks
0 1 2 3 4
В О Р О Н
secretWord
0 1 2 3 4
Рис. 1.8
146
if 'н' in 'он': # True
blanks = '_о_о_' + 'н' + '' # Код у рядку виконується.
# Змінній blanks присвоєно значення _о_он '.
Код з наведеного вище прикладу робить те саме, коли зна-
чення secretWord одне – 'ворон', а значення correctLetters одне –
'он'. Наступний фрагмент коду виводить на екран нове значення
blanks із пробілами між літерами.
60. for letter in blanks: # Показує секретне слово з пробілами
між літерами
61. print (letter, end = '')
62. print ()
Зазначимо, що цикл for у рядку 60 не викликає функцію range ().
Замість ранжирування операцій послідовністю, що повертає range,
ітерації проводяться за значенням рядкової змінної blanks. При ко-
жній ітерації береться одна нова літера з рядка 'ворон' змінної
blanks. У результаті після додавання пробілів буде виведено '_о_он'.
Отримання припущень гравця. При виконанні функції
getGuess () гравець може ввести передбачувану літеру. Функція
повертає літеру, запропоновану гравцем, у вигляді рядка.
Далі функція getGuess () перевіряє допустимість уведеного
символу, перш ніж передати його у функцію.
64. def getGuess (alreadyGuessed):
65. # Повертає літеру, уведену гравцем. Функція перевіряє,
чи гравець увів тільки одну літеру і нічого більше.
Рядок літер, запропонованих гравцем, передається як аргумент
у параметр alreadyGuessed функції. Потім функція getGuess () про-
понує гравцю ввести одну літеру, яку поверне як своє значення.
Оскільки Python чутливий до регістру, то слід переконатися, що
введена мала літера, щоб її можна було коректно зіставити з літе-
рами секретного слова. Для цього знадобиться метод lower ().
Рядкові методи lower () та upper (). Метод lower () повертає
рядок, у якому всі літери рядкові. Існує також зворотний рядко-
вий метод upper (), який повертає рядок з великими літерами.
Завершення циклу while. У рядку 66 цикл while буде вима-
гати від гравця ввести літеру до тих пір, поки не буде введена
літера, яка раніше не пропонувалася.
147
Умовою циклу while є логічна змінна в значенні True. Це
означає, що єдиним способом завершити цикл є виконання ін-
струкції break або інструкції return, яка забезпечує вихід не тіль-
ки з циклу, але й із функції.
Внутрішній код циклу пропонує гравцеві ввести літеру, яка
буде збережена в змінній guess. Якщо гравець уведе велику лі-
теру, то вона буде перетворена на малу за допомогою коду в
рядку 69.
Інструкції elif. Наступна частина коду гри "Шибениця" міс-
тить інструкції elif. Інструкція elif означає: якщо це істинно, то
зробити так, а якщо виконується інша умова, то зробити так;
якщо ж не виконується жодна умова, то зробити ось так.
Якщо умова однієї інструкції elif істинна, то виконується код
саме цієї інструкції, а потім відбувається повернення до першо-
го рядка циклу. Таким чином, за один прохід виконується код
одного й тільки одного програмного блоку конструкції if-elif-
else. Інструкція else припиняє роботу блоку if, якщо більше не
треба перевіряти виконання його умов.
Перевірка допустимості припущення гравця. Змінна guess
містить літери, запропоновані гравцем. Програма має впевнити-
ся, що вводяться допустимі символи. Може бути тільки одна
літера, при цьому не мають застосовуватися літери, що вводи-
лись раніше. Якщо ця умова не виконується, то цикл поверта-
ється до початку і знову запитує літеру.
70. if len (guess)! = 1:
71. print ('Будь ласка, введіть одну літеру.')
72. elif guess in alreadyGuessed:
73. print ('Ви вже називали цю літеру. Назвіть іншу.')
74. elif guess not in 'абвгдеежзійклмнопрстуфхцчшщ'иьеюя':
75. print ('Будь ласка, введіть ЛІТЕРУ.')
76. else:
77. return guess
У рядку 70 перевіряється, чи введено не більше одного сим-
волу; код у рядку 72 перевіряє, чи запропонована літера не міс-
титься у змінній alreadyGuessed, код у рядку 74 – чи введено
символ стандартного українського алфавіту.
148
Якщо хоча б одна умова не виконується, то гравцеві пропо-
нується ввести іншу літеру. Якщо виконано всі умови, то про-
грама переходить до виконання коду блоку і повертає значення
запропонованої літери в рядку 77.
Нагадаємо, що в конструкції if-elif-else виконується тільки
один програмний блок.
Пропозиція гравцеві зіграти знову. Функція playAgain ()
містить лише виклик функції print () та інструкцію return.
79. def playAgain ():
80. # Ця функція повертає значення True, якщо гравець хоче
зіграти знову; в іншому випадку повертає False.
81. print ('Бажаєте зіграти ще? (Так чи ні)')
82. return input (). Lower (). Startswith ('д')
Інструкція return завершує виконання програми, але її дію
можна скасувати. Розглянемо докладніше, які перетворення
здійснює Python, якщо гравець відповів ТАК.
input (). lower (). startswith ('y')
'YES'.lower (). Startswith (' y ')
'Yes'.startswith (' y ')
True
т
т
ат
ТАК
Функція playAgain () дозволяє гравцеві відповісти Так або Ні
на пропозицію ще одного раунду. Гравець повинен увести Так,
так, Т або що-небудь, що починається з літери т, і це значення
буде означати Так. Якщо гравець увів ТАК, то функція input ()
повертає значення 'ТАК'. Вираз 'ТАК'.lower () повертає рядок у
нижньому регістрі (маленькими літерами), тобто значення 'ТАК'
перетворюється на "так". Крім цього, виконується ще й виклик
методу startswith ('т').
Ця функція повертає True, якщо викликаний рядок почина-
ється зі вказаного параметра, або False, якщо це не так. Напри-
клад, вираз "так".
startwith ('т') повертає значення True.
149
Це єдиний сенс виразу. Він дозволяє гравцеві ввести відпо-
відь, переводить його у рядкову форму, перевіряє, чи відповідь
починається з літери "т", і потім повертає True або False.
Огляд функцій гри. Коротко розглянемо функції, що вико-
ристовуються у грі "Шибениця".
• getRandomWord (wordList) випадковим чином вибирає один ря-
док зі списку. Так вибирається слово, яке буде вгадувати гравець.
• displayBoard (missedLetters, correctLetters, secretWord) відобра-
жає поточний стан гри, показує, скільки літер гравець уже запро-
понував і які з них виявилися помилковими. Для коректної роботи
функції необхідні три параметри: рядкові змінні correctLetters і
missedLetter для зберігання літер, які були запропоновані гравцем, і
тих, яких не виявилося в секретному слові, а також рядкова змінна
secretWord, що містить секретне слово, яке намагається вгадати
гравець. Функція не повертає будь-яких значень.
• getGuess (alreadyGuessed) перевіряє, чи не міститься введена
гравцем літера в рядковій змінній alreadyGuessed. Функція пове-
ртає літеру, уведену гравцем, якщо вона допустима.
• playAgain () запитує гравця, чи не хоче він зіграти ще раз.
Функція повертає True, якщо гравець погоджується, або False,
якщо відмовляється.
Після опису функцій з рядка 85 починається основна частина
(тіло) програми. Усе, що вище цього рядка, – це опис функцій і
код інструкції присвоювання для змінної HANGMAN_PICS.
Ігровий цикл. Основна частина коду програми "Шибениця"
відображає на екрані назву гри, містить кілька змінних і запус-
кає цикл while. Розглянемо послідовне виконання решти про-
грамного коду.
85. print ('Ш И Б Е Н И Ц Я')
86. missedLetters = ''
87. correctLetters = ''
88. secretWord = getRandomWord (words)
89. gameIsDone = False
Код у рядку 85 викликає функцію print (), яка виводить на ек-
ран заголовок гри в момент її запуску. Потім рядковій змінній
missedLetters присвоюється порожнє значення; змінній
correctLetters теж присвоюється порожнє значення, оскільки
гравець не запропонував ще ніяких літер.
150
У рядку 88 викликається функція getRandomWord (words),
яка вибирає випадковим чином секретне слово зі списку.
Код у рядку 89 присвоює змінній gameIsDone значення False;
він присвоїть їй значення True, коли надійде сигнал про завер-
шення гри, і запропонує гравцеві зіграти заново.
Виклик функції displayBoard (). Частина програми, що за-
лишилася, складається із циклу while. Умова циклу завжди іс-
тинна, а це значить, що він буде виконуватися нескінченно дов-
го, доти, поки не буде ініційовано виконання інструкції break (це
відбувається в рядку 126).
91. while True:
92. displayBoard(missedLetters, correctLetters, secretWord)
Код у рядку 92 викликає функцію displayBoard (), передаючи їй
значення трьох змінних, установлених у рядках 86–88. Залежно від
того, скільки літер гравець угадав правильно і скільки разів поми-
лився, функція виводить на екран зображення повішеного.
Введення гравцем літери, яка вгадується. Далі виклика-
ється функція getGuess (), щоб гравець міг увести літеру, що
вгадується.
94. # Дозволяє гравцеві ввести літеру.
95. guess = getGuess (missedLetters + correctLetters)
У функцію передається параметр alreadyGuessed для визна-
чення, вводив або ні гравець таку літеру раніше. Код у рядку 95
конкатенує рядкові змінні missed Letters і correct Letters, а потім
передає результат як аргумент параметру alreadyGuessed.
Перевірка наявності літери в секретному слові. Якщо за-
пропонована літера є в секретному слові, то вона додається в
кінець рядкової змінної correctLetters.
97. if guess in secretWord:
98. correctLetters = correctLetters + guess
Код в останньому рядку додає нове значення у змінну
correctLetters.
Перевірка факту перемоги гравця. Як програмі дізнатися,
що гравець угадав усі літери, які містяться в секретному слові?
Змінна correctLetters містить усі літери, що вгадані гравцем пра-
вильно, а змінна secretWord – секретне слово. Однак просту пе-
151
ревірку missedLetters == correctLetters зробити неможливо. Як-
що, наприклад, секретне слово 'ворон', а correctLetters – 'внро', то
значення виразу correctLetters == secretWord буде помилковим,
хоча гравець угадав усі літери.
Єдино можливий шлях – виконати порівняння кожної літери
із correctLetters із літерами в змінній secretWord. Гравець пере-
магає тоді й тільки тоді, коли кожна літера зі змінної secretWord
міститиметься в змінній correctLetters.
100. # Перевіряє, чи виграв гравець.
101. foundAllLetters = True
102. for i in range(len(secretWord)):
103. if secretWord[i] not in correctLetters:
104. foundAllLetters = False
105. break
Зрозуміло, що якщо в змінній secretWord знайдена літера,
якої немає в змінній correctLetters, то гравець не переміг. Нова
змінна foundAllLetters зі значенням True встановлюється в рядку
101 до початку циклу. Цикл починається в припущенні, що всі
літери секретного слова вгадані. Однак у рядку 104 у процесі
виконання циклу значення змінної foundAllLetters змінюється на
False, як тільки виявляється перша літера зі змінної secretWord,
що не міститься в змінній correctLettrs.
Якщо всі літери секретного слова виявлені, то гравцеві по-
відомляється про його перемогу, а змінна gameIsDone набуває
значення True.
106. if foundAllLetters:
107. print ('ТАК! Секретне слово – "' + secretWord + '"! Ви
вгадали!')
108. gameIsDone = True
Обробка помилкових припущень. У рядку 109 починається
блок else.
109. else:
110. missedLetters = missedLetters + guess
Нагадаємо, що код цього блоку виконується, якщо умова ци-
клу for – логічне "ні" ("хиба"). Проте яка умова? Щоб дізнатися
це, наведіть курсор на початок ключового слова else і переміс-
152
тіть його вгору. Ви побачите, що ключове слово else розташову-
ється в тій самій позиції, що if, у рядку 97.
97. if guess in secretWord:
–пробіл–
109. else:
110. missedLetters = missedLetters + guess
Таким чином, якщо умова в рядку 97 (guess in secretWord)
"хиба", то виконується блок else.
Неправильно вгадані літери додаються в рядкову змінну
missedLetters у рядку 110. Це відбувається так само, як у рядку
98 з літерами, що вгадані правильно.
Перевірка факту програшу гравця. Кожного разу, коли
гравець уводить неправильну літеру, вона додається в змінну
missedLetters. Таким чином, довжина значення змінної
missedLetters (або в коді – len (missedLetters)) стає рівною кіль-
кості помилкових припущень.
112. # Перевіряє перевищення гравцем ліміту спроб і факту
програшу.
113. if len (missedLetters) == len (HANGMAN_PICS) – 1:
114. displayBoard (missedLetters, correctLetters, secretWord)
115. print ('Ви вичерпали всі спроби! \ N не вгадано літер:' +
str (len (missedLetters)) + 'і вгадано
літер: '+ str (len (correctLetters)) +'. Було загадане слово
"'+ secretWord +'". ')
116. gameIsDone = True
Змінна HANGMAN_PICS містить сім рядків із ASCII-
символами рисунку. Отже, якщо довжина рядка missedLetters
дорівнює len (HANGMAN_PICS) – 1 (тобто 6), то гравець виче-
рпав ліміт припущень. Якщо рисунок повішеного завершено, то
гравець програв. Нагадаємо, що HANGMAN_PICS [0] – перший
елемент списку, а HANGMAN_PICS [6] – останній.
Код у рядку 115 виводить секретне слово, а рядок 116 при-
своює змінній gameIsDone значення True.
118. # Запитує, чи хоче гравець зіграти заново (тільки якщо
гра завершена).
119. if gameIsDone:
153
120. if playAgain():
121. missedLetters = ''
122. correctLetters = ''
123. gameIsDone = False
124. secretWord = getRandomWord(words)
Завершення або перезавантаження гри. Незалежно від пе-
ремоги або програшу гра має запитати гравця, чи хоче він зігра-
ти знову. Функція playAgain () обробляє відповідь ("Так" або
"Ні") урядку 120.
Якщо гравець хоче почати гру заново, то значення змінних
missedLetters і correctLetters треба обнулити, змінній gameIsDone
присвоїти значення False і вибрати нове секретне слово. Коли
виконання циклу while повертається до початку, до рядка 91,
ігровий інтерфейс, який відображається на екрані, перезаванта-
жується і готовий до нової гри.
Якщо гравець не ввів нічого, що починається з літери "т", у
відповідь на запит, чи хоче він зіграти заново, то умова в рядку
120 стає хибною і виконується блок else.
125. else:
126. break
Інструкція break зумовлює виконання першої інструкції пі-
сля циклу. Однак оскільки після циклу немає виконуваних ін-
струкцій, то відбувається завершення роботи програми.
Можливі доопрацювання гри. Зігравши у гру "Шибениця"
кілька разів, ви, можливо, подумали, що шести спроб недостат-
ньо для вгадування багатьох слів. Кількість спроб можна легко
збільшити, додавши рядки у список HANGMAN_PICS.
Збережіть файл hangman.py під ім'ям hangman2.py. Для роз-
ширення списку, що містить ASCII-символи для прорисовуван-
ня повішеника, додайте такі рядки, починаючи з 37-го:
37. ===''', '''
38. +–-+
39. [O |
40. /|\ |
41. / \ |
42. ===''', '''
154
43. +–-+
44. [O] |
45. /|\ |
46. / \ |
47. ===''']
Цей код додає два нові багаторядкові значення до списку
HANGMAN_PICS, один рядок служить для прорисовування
лівого вуха повішеника (виділено червоним кольором – рядок
39), інший – для прорисовування обох вух (рядок 44). Програма
повідомляє гравцеві про його програші на підставі значення
виразу len (missedLetters) == len (HANGMAN_PICS) – 1, тому в
інших змінах необхідності немає. Інша частина програми буде
прекрасно працювати з новим списком.
Словники. У першій версії програми "Шибениця" ми вико-
ристовували список з назвами тварин, але можна змінити список
слів у рядку 48. Наприклад, замість тварин можна використову-
вати кольори:
48. words = 'червоний помаранчевий жовтий зелений синій
блакитний фіолетовий білий чорний коричневий'.split ()
або геометричні фігури:
48. words = 'квадрат трикутник прямокутник коло еліпс ромб
трапеція паралелограм п'ятикутник шестикутник восьмикут-
ник'.split ()
або фрукти:
48. words = 'яблуко апельсин лимон лайм груша мандарин
виноград грейпфрут персик банан абрикос манго банан некта-
рин'.split ()
Можна доопрацювати код так, що гра "Шибениця" буде ви-
користовувати кілька наборів слів (назви тварин, кольорів, фі-
гур, фруктів тощо). Програма повідомлятиме гравцеві, який
набір використано для вибору секретного слова. Щоб це зроби-
ти, знадобиться новий тип даних, який називається словником.
Словник, як і список, – це набір значень, але замість доступу до
елементів за цілочисловими індексами в словнику можливий
доступ до елементів за індексами довільного типу. У словнику
ці індекси називаються ключами.
155
Відмінності між списком і словником. При роботі зі словни-
ком застосовуються фігурні дужки {} замість квадратних []. Ще
одна відмінність словника від списку – можливість використання як
ключів значень довільного типу, а не тільки цілих чисел. Нагадає-
мо, що 0 та '0' – різні величини, тобто це будуть різні ключі.
Словники, подібно спискам, можна обробляти циклічно, ви-
користовуючи ключі в циклі for.
Ключі та значення можуть виводитися в різному порядку, тому
що словники, на відміну від списків, не впорядковані (не ранжова-
ні). Першим елементом списку з ім'ям listStuff буде listStuf [0]. Од-
нак у словнику немає першого елемента, тому що немає певного
порядку розташування елементів. У наведеному коді Python просто
вибрав той порядок, у якому елементи зберігалися в пам'яті, і не-
має жодних гарантій, що наступного разу порядок буде тим самим.
Словники не впорядковані та вважаються еквівалентними,
якщо складаються з однакових пар "ключ – значення", а списки
з однаковими значеннями елементів, але різним порядком їхньо-
го розташування, еквівалентними не будуть.
Методи словника keys () та values (). Словники містять два
корисні методи – keys () та values (). Ці методи повертають зна-
чення, типи яких називаються dict_keys та dict_values, відповід-
но. Як і більшість ранжируваних об'єктів, дані цих типів повер-
таються у формі списку функцією list (). Використовуючи мето-
ди keys () або values () у функції len (), можна отримати список
ключів або значень словника.
Використання словника слів у грі "Шибениця". Змінимо
код у новій версії гри "Шибениця", додавши підтримку різних
наборів секретних слів. По-перше, замінимо значення змінної
words, створивши словник, у якому ключі зображені рядками, а
значення – списками рядків. Рядковий метод split () поверне
список рядків, по одному слову в кожному рядку.
48. words = { 'Кольори': 'червоний помаранчевий жовтий зеле-
ний синій блакитний фіолетовий білий чорний коричневий'.split (),
49. 'Фігури': 'квадрат трикутник прямокутник коло еліпс ромб тра-
пеція паралелограм п'ятикутник шестикутник восьмикутник'.split (),
50. 'Фрукти': 'яблуко апельсин лимон лайм груша мандарин вино-
град грейпфрут персик банан абрикос манго банан нектарин'.split (),
156
51. 'Тварини': 'лелека бабуїн баран борсук бик вовк зебра кит
коза корова кішка кролик щур лев лисиця лось ведмідь мул ми-
ша норка носоріг мавпа вівця олень віслюк панда пума скунс
собака сова тигр тюлень тхір ящірка'.split ()}
Рядки 48–51 – це одна інструкція присвоювання. Інструкція
закінчується фігурною дужкою, що закривається в рядку 51.
Випадковий вибір зі списку. Функція choice () модуля
random приймає як аргумент список і повертає з нього випадко-
ве значення. Це схоже на те, що раніше робила функція
getRandomWord (). У новій версії функції getRandomWord ()
буде використана функція choice (). Подібно до того, як функція
randint () повертає випадкове ціле число, функція choice () пове-
ртає випадкове значення зі списку.
Зміни функції getRandomWord () такі, що тепер її параметр – сло-
вник, що складається зі списків рядків, а не просто список рядків.
Так виглядала оригінальна функція:
40. def getRandomWord (wordList):
41. # Ця функція повертає випадковий рядок з переданого списку.
42. wordIndex = random.randint (0, len (wordList) – 1)
43. return wordList [wordIndex]
А так виглядає її код після зміни:
53. def getRandomWord (wordDict):
54. # Ця функція повертає випадковий рядок з переданого
словника списків рядків, а також ключ.
55. # По-перше, випадковим чином вибираємо ключ зі словника:
56. wordKey = random.choice (list (wordDict.keys ()))
57.
58. # По-друге, випадковим чином вибираємо слово зі списку
ключів у словнику:
59. wordIndex = random.randint (0, len (wordDict [wordKey]) – 1)
Ім'я wordList змінено на wordDict для більшої наочності.
Тепер замість випадкового вибору слова зі списку рядків споча-
тку випадково вибирається ключ зі словника wordDict за допомо-
гою виклику random.choice (), а замість повернення рядка wordList
[wordIndex] функція повертає список із двома елементами:
перший елемент – wordDict [wordKey] [wordIndex];
другий елемент – wordKey.
157
Вираз wordDict [wordKey] [wordIndex] у рядку 61 може зда-
ватися складним, але результат його дії зрозуміти дуже просто.
По-перше, уявімо, що змінній wordKey присвоєно значення
'Фрукти', а змінній wordIndex – значення 5. Тоді перетворення
wordDict [wordKey] [wordIndex] матиме такий вигляд:
wordDict[wordKey][wordIndex]
wordDict['Fruits'][wordIndex]
['apple', 'orange', 'lemon', 'lime', 'pear', 'watermelon', 'grape',
'grapefruit', 'cherry', 'banana', 'cantaloupe', 'mango', 'strawberry',
'tomato'][wordIndex]['apple', 'orange', 'lemon', 'lime', 'pear',
'watermelon', 'grape', 'grapefruit', 'cherry', 'banana', 'cantaloupe',
'mango', 'strawberry', 'tomato'][5]
'watermelon'
'Фрукти'
'мандарин'
['яблуко', 'апельсин', 'лимон', 'лайм', 'груша', 'мандарин', 'вино-
град', 'грейпфрут', 'персик', 'банан', 'абрикос', 'манго', 'банан',
'нектарин'][wordIndex]
['яблуко', 'апельсин', 'лимон', 'лайм', 'груша', 'мандарин', 'вино-
град', 'грейпфрут', 'персик', 'банан', 'абрикос', 'манго', 'банан',
'нектарин'][5]
У цьому випадку елемент списку, що повертається функцією, –
це рядок 'мандарин' (нагадаємо, що індекси починаються з 0, тому
індекс [5] відсилає до шостого елемента списку, а не до п'ятого).
Оскільки функція getRandomWord () тепер повертає список із
двох елементів, а не рядок, то і значення змінної secretWord буде
списком, а не рядком. Щоб зберегти ці значення у двох різних
змінних, можна використовувати множинне присвоювання, яке
розглянемо далі.
Видалення елементів списку. Інструкція del видаляє елеме-
нти із зазначеними індексами зі списку. Оскільки del – інструк-
ція, а не функція і не оператор, то вона вказується без круглих
дужок і не має значення, що повертається.
Зазначимо, що коли видаляється елемент з індексом 1, елемент,
який мав індекс 2, стає елементом з індексом 1, а елемент, який мав
індекс 3, отримує індекс 2 і т. д. Вищеописана процедура видаляє
тільки один елемент і, відповідно, зменшує кількість індексів.
158
Довжина списку HANGMAN_PICS – це кількість зроблених
гравцем припущень. Видаляючи рядки з цього списку, можна
скорочувати допустиму кількість припущень і, тим самим,
ускладнювати гру.
Додайте такі рядки в код вашої програми між рядком print
('Ш И Б Е Н И Ц Я') і рядком missedLetters = ''.
103. print ('' Ш И Б Е Н И Ц Я ')
104.
105. difficulty = ''
106. while difficulty not in 'ЛСТ':
107. print ('Виберіть рівень складності: Л – Легкий, С – Сере-
дній, В – Важкий')
108. difficulty = input (). Upper ()
109. if difficulty == 'С':
110. del HANGMAN_PICS [8]
111. del HANGMAN_PICS [7]
112. if difficulty == 'Т':
113. del HANGMAN_PICS [8]
114. del HANGMAN_PICS [7]
115. del HANGMAN_PICS [5]
116. del HANGMAN_PICS [3]
117.
118. missedLetters = ''
Цей код видаляє елементи списку HANGMAN_PICS, робля-
чи його коротше, залежно від обраного рівня складності гри.
Чим вище рівень складності, тим більше елементів видаляється
зі списку HANGMAN_PICS, скорочуючи можливу кількість
спроб угадування. Інша частина коду гри "Шибениця" викорис-
товує довжину цього списку для визначення моменту виведення
повідомлення гравцеві про вичерпання кількості спроб.
Множинне присвоювання. Множинне присвоювання – це
можливість призначення кільком змінним різних значень в од-
ному рядку. Для множинного присвоювання запишіть змінні
через кому і надайте їм список значень. Нижче наведено при-
клад запису, уведіть її в інтерактивній оболонці:
>>> fruit, animal, number = ['апельсин', 'кіт', 42]
>>> fruit
159
'апельсин'
>>> animal
'кіт'
>>> number
42
Виконання коду, наведеного в прикладі, рівноцінно виконан-
ню таких операцій присвоювання:
>>> fruit = ['апельсин', 'кіт', 42] [0]
>>> animal = ['апельсин', 'кіт', 42] [1]
>>> number = ['апельсин', 'кіт', 42] [2]
Кількість рядків з іменами змінних у лівій частині інструкції
присвоювання має бути еквівалентна кількості елементів, зазна-
чених у правій частині. Python автоматично присвоїть значення
першого елемента списку першій змінній, другій змінній – зна-
чення другого елемента і т. д. Якщо кількість змінних буде від-
мінною від кількості елементів списку, то Python інтерпретує це
як помилку.
Зміна рядків 120 та 157 у коді гри "Шибениця" для викорис-
тання множинного присвоювання значень, що повертаються
функцією getRandomWord (), має такий вигляд:
119. correctLetters = ''
120. secretWord, secretSet = getRandomWord(words)
121. gameIsDone = False
–пробіл–
156. gameIsDone = False
157. secretWord, secretSet = getRandomWord(words)
158. else:
159. break
У рядку 120 значення, що повертаються функцією
getRandomWord (word), присвоюються двом змінним –
secretWord і secretSet. Код у рядку 157 робить те саме, коли гра-
вець вирішує зіграти заново.
Вибір гравцем категорії слів. Остання зміна, яку необхідно
зробити, – повідомити гравцеві, до якого набору належить слово
для вгадування (тварина, колір, фігура або фрукт). Нижче пока-
заний початковий код:
160
91. while True:
92. displayBoard(missedLetters, correctLetters, secretWord)
У новій версії додайте рядок 124, щоб код був таким:
123. while True:
124. print('Секретне слово з набору: ' + secretSet)
125. displayBoard(missedLetters, correctLetters, secretWord)
На цьому зміни в програмі "Шибениця" закінчуються. За-
мість простого списку рядкових змінних секретне слово вибира-
ється з кількох різних списків. Програма повідомляє гравцеві,
який набір слів використаний для вибору секретного слова.
Спробуйте зіграти в новій версії.
Ви можете легко змінити словник слів, що починається в ря-
дку 48, щоб ще збільшити кількість наборів слів.
Отже, написання коду гри "Шибениця" завершено. Ви ви-
вчили кілька нових понять, додаючи при цьому в гру нові мож-
ливості. Навіть написавши програму, ви можете розширювати її
функціонал у процесі вивчення Python.
Словники схожі на списки, за винятком того, що словники
можуть використовувати індекси довільного типу, а не тільки
цілочислові значення. Індекси словників називаються ключами.
Багатозначне присвоювання – це раціональний спосіб присвою-
вання різним змінним значень, поміщених у список.
Гра "Шибениця" стала значно довершенішою порівняно з
попередньою версією. Тепер вам знайомі основні концептуальні
поняття, необхідні при написанні програм: змінні, цикли, функ-
ції, типи даних, список і словник.
161
Рис. 1.9
162
Викликом tk.title ("Гра") задаємо заголовок ігрового вікна
(для цього служить функція title раніше створеного об'єкта tk).
Викликаємо функцію resizable, щоб зробити розмір вікна фіксо-
ваним. Аргументи 0, 0 означають: розмір вікна має бути незмін-
ним як по горизонталі, так і по вертикалі. Викликаємо функцію
wm_attributes, указуючи, що вікно з полотном треба розмістити
зверху над усіма іншими вікнами ("-topmost").
Зверніть увагу, що, створюючи полотно, ми передали у фун-
кцію більше іменованих аргументів, ніж у попередніх прикла-
дах. Наприклад, аргументи bd = 0 та highlightthickness = 0 потрі-
бні для того, щоб навколо полотна не було рамки (від цього на-
ша гра матиме кращий вигляд).
У результаті виклику canvas.pack () полотно змінить розмір від-
повідно до значень ширини та висоти, зазначених у попередньому
рядку коду. Команда tk.update () готує tkinter до ігрової анімації.
Без виклику update програма не працюватиме як задумано.
Не забувайте зберігати код у процесі його написання. При
першому зберіганні дайте файлу ім'я, наприклад paddleball.py.
Створюємо клас для м'яча. Щоб створити клас для м'яча,
насамперед додамо до нього код відтворення м'яча на полотні,
для чого треба:
• створити клас під назвою Ball, який приймає як аргументи
функції __init__ полотно і колір м'яча;
• зберегти у властивості об'єкта полотно, щоб надалі рисува-
ти на ньому м'яч;
• зобразити на полотні коло, заповнене переданим у аргумен-
ті кольором;
• зберегти ідентифікатор, який поверне функція рисування кола,
оскільки за його допомогою ми будемо переміщати м'яч по екрану;
• перемістити нарисоване коло в центр полотна.
Цей код треба додати в початок файлу, після рядка import time:
from tkinter import *
import random
import time
1 class Ball:
2 def __init__(self, canvas, color):
3 self.canvas = canvas
163
4 self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
5 self.canvas.move(self.id, 245, 100)
def draw(self):
pass
У рядку 1 даємо класу ім'я Ball. У рядку 2 створюємо функ-
цію ініціалізації __init__, яка приймає як аргументи полотно і
колір. У рядку 3 зберігаємо аргумент canvas у властивості з та-
ким самим ім'ям.
У рядку 4 викликаємо функцію create_oval для рисування ко-
ла, передаючи їй п'ять аргументів: x- та y-координати лівого
верхнього кута прямокутника (10 і 10), x- та y-координати його
правого нижнього кута (25 і 25) і колір заповнення.
Функція create_oval повертає ідентифікатор нарисованої фі-
гури, який ми зберігаємо у властивості id. У рядку 5 переміщає-
мо коло приблизно в центр полотна (позиція 245, 100), передав-
ши для цього у функцію move збережений раніше ідентифікатор
фігури (властивість id).
У двох останніх рядках визначаємо функцію draw (def draw
(self)), указуючи замість її тіла ключове слово pass (поки функ-
ція draw нічого не робить, але скоро ми це виправимо).
Отже, у нас є клас Ball, і тепер треба створити його об'єкт
(згадайте: клас лише описує, що треба робити, а об'єкт виконує
конкретні дії). Щоб створити об'єкт – м'яч червоного кольору –
додайте в кінець програми такий код:
ball = Ball(canvas, 'red')
Якщо тепер запустити програму, то полотно на мить з'явиться
і знову зникне. Щоб вікно не закривалося, треба додати до про-
грами цикл ігрової анімації, який називають головним циклом гри.
Головний цикл – центральний елемент програми, який керує бі-
льшою часткою дій. Спочатку наш головний цикл буде тільки пе-
рерисовувати екран. Цей цикл нескінченний (принаймні він буде
виконуватися до тих пір, поки ми не закриємо вікно), і при кожно-
му його повторі ми будемо давати команду перерисувати екран і
робити паузу на одну соту частку секунди. Додайте цей код у кі-
нець програми:
ball = Ball(canvas, 'red')
164
while 1:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Якщо тепер запустити код, то приблизно в центрі полотна
має з'явитися м'яч (рис. 1.10).
Рис. 1.10
170
tk.wm_attributes("-topmost", 1)canvas = Canvas(tk, width=500,
height=400, bd=0,
highlightthickness=0)
canvas.pack()
tk.update()
ball = Ball(canvas, 'red')
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Створюємо ракетку. Почнемо з класу для ракетки (Paddle), код
якого треба ввести відразу після класу Ball і його функції draw:
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.y = -3
if pos[0] <= 0: self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10,
fill=color)
self.canvas.move(self.id, 200, 300)
def draw(self):
pass
Цей код дуже схожий на код класу Ball. Однак замість функ-
ції create_oval ми використовуємо create_rectangle і переміщаємо
нарисований нею прямокутник у позицію 200, 300 (200 пікселів
від лівого краю полотна і 300 пікселів – від верхнього краю).
171
Ближче до кінця програми створимо об'єкт класу Paddle і до-
повнимо ігровий цикл викликом функції draw цього об'єкта:
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, 'red')
while 1:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Якщо тепер запустити гру, то побачимо літаючий м'яч і роз-
міщену на одному місці прямокутну ракетку (рис. 1.11).
Рис. 1.11
173
Для переміщення ракетки відповідно до значення властивості x
використовуємо функцію move – self.canvas.move (self.id, self.x, 0).
Отримуємо координати ракетки (зберігаючи їх у змінній pos), щоб
перевірити, чи досягла ракетка лівої або правої границі полотна.
На відміну від м'яча, ракетка при зіткненні з границею має не ві-
дскочити від неї, а зупинитися. Тому, якщо ліва x-координата (pos
[0]) менше або дорівнює 0 (<= 0), то обнуляємо властивість x (self.x
= 0). Якщо права x-координата ракетки (pos [2]) більше або дорів-
нює ширині полотна (> = self.canvas_width), також обнуляємо x.
Примітка. Якщо ви запустите гру зараз, то, можливо, дове-
деться кликнути по її вікну, щоб програма почала реагувати на
натискання клавіш. Клик робить вікно активним, після чого
воно може обробляти події клавіатури.
Перевірка на зіткнення м'яча з ракеткою. Зараз наш код вла-
штований так, що м'яч не стикається з ракеткою, а пролітає крізь неї.
Щоб цього не відбувалося, м'яч має "знати" про зіткнення з ракет-
кою так само, як він "знає" про зіткнення з границями полотна.
Проблему можна розв'язати, додавши відповідний код у функ-
цію draw (де виконується перевірка зіткнень із границями). Однак
краще створити окремі функції, щоб програма складалася з неве-
ликих частин. Справа в тому, що в програмі дуже складно розібра-
тися, якщо в одному місці (наприклад в одній функції) накопичу-
ється занадто багато рядків коду. Унесемо необхідні зміни.
Додамо у функцію __init__ класу Ball ще один аргумент –
об'єкт-ракетку:
class Ball:
1def __init__(self, canvas, paddle, color):
self.canvas = canvas
2self.paddle = paddle
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
174
Зверніть увагу, що в рядку 1 ми змінили аргументи __init__,
додавши до них ракетку (paddle), а в рядку 2 зберегли значення
аргументу paddle у властивості з такою самою назвою.
Тепер треба змінити код створення об'єкта-м'яча з урахуван-
ням нового аргументу – ракетки. Цей код міститься в кінці про-
грами перед головним циклом:
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, paddle, 'red')
while 1:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Код для перевірки зіткнення з ракеткою буде складнішим,
ніж для перевірки границь полотна. Помістимо його в нову фу-
нкцію hit_paddle, додавши її виклик у функцію draw класу Ball,
разом з перевіркою на зіткнення з нижньою границею:
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.y = -3
if self.hit_paddle(pos) == True:
self.y = -3
if pos[0] <= 0:
self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
Якщо hit_paddle повертає True, то ми змінюємо напрямок
польоту м'яча, задаючи властивості y значенні -3 (self.y = -3). Не
намагайтеся зараз запустити гру – ми ще не створили функцію
hit_paddle. Давайте це виправимо.
Додайте код функції hit_paddle відразу перед функцією draw:
1 def hit_paddle(self, pos):
175
2 paddle_pos = self.canvas.coords(self.paddle.id)
3 if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
4 if pos[3] >= paddle_pos[1] and pos[3] <=
paddle_pos[3]:
return True
return False
У рядку 1 оголошуємо функцію з аргументом pos, у якому
будемо передавати поточні координати м'яча. У рядку 2 отриму-
ємо координати ракетки та зберігаємо їх у змінній paddle_pos.
У рядку 3 міститься конструкція if, умова якої означає:
x-координата правого боку м'яча більше, ніж x-координата
лівого боку ракетки, а x-координата лівого боку м'яча менше,
ніж x-координата правого боку ракетки. Тут pos [2] відповідає x-
координаті правого боку м'яча, а pos [0] – x-координаті його
лівого боку. При цьому paddle_pos [0] відповідає x-координаті
лівого боку ракетки, а paddle_pos [2] – х-координаті її правого
боку. На рис. 1.12 зображена схема, де показані координати для
випадку, коли м'яч ось-ось торкнеться ракетки.
Рис. 1.12
178
Рис. 1.14
179
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.hit_bottom = True
if self.hit_paddle(pos) == True:
self.y = -3
if pos[0] <= 0:
self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill=color)
self.canvas.move(self.id, 200, 300)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
def draw(self):
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
180
def turn_right(self, evt):
self.x = 2
tk = Tk()
tk.title("Гра")
tk.resizable(0, 0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0,
highlightthickness=0)
canvas.pack()
tk.update()
paddle = Paddle(canvas, 'blue')
ball = Ball(canvas, paddle, 'red')
while 1:
if ball.hit_bottom == False:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Можливі вдосконалення гри. Зараз гра украй проста. Щоб
вона мала професійний вигляд, варто її доопрацювати.
1. Затримка перед початком гри. Гра починається одразу після
запуску, проте без клацання по полотну клавіші-стрілки можуть
не розпізнаватися програмою. Спробуйте додати затримку перед
стартом гри, щоб у гравця вистачило часу клацнути по полотну.
Ще краще, скориставшись прив'язкою до подій, зробити так, щоб
гра починалася при натисканні мишкою всередині вікна.
Підказка 1. Ми вже додавали код для прив'язки до подій у клас
ракетки (Paddle), його можна використовувати як відправну точку.
Підказка 2. Для прив'язки до натиснення лівої кнопки мишки
використовуйте ім'я події '<Button-1>'.
2. Екран "Кінець гри". Зараз після закінчення гри екран за-
стигає. Користувачеві буде набагато зручніше, якщо при тор-
канні м'ячиком нижнього краю полотна на екрані з'явиться по-
відомлення "Кінець гри". У цьому вам допоможе функція
181
create_text. Варто звернути увагу й на функцію itemconfig з ар-
гументом state, у якому можна передавати такі значення, як
normal та hidden.
Як додаткове завдання спробуйте витримати перед показом
повідомлення невелику паузу.
3. Прискорення м'яча. Якщо вам доводилося грати в теніс, то
ви знаєте, що часом м'яч відлітає від ракетки швидше, ніж руха-
вся раніше. Це залежить від сили удару по ньому. У нашій грі
м'яч рухається з постійною швидкістю. Доопрацюйте програму
так, щоб швидкість м'яча при відскоку змінювалася залежно від
рухів ракетки.
4. Рахунок у грі. Додайте у гру підрахунок очок. Кожного ра-
зу, коли м'яч відскакує від ракетки, рахунок має зростати. Зро-
біть так, щоб набрані очки відображалися в правому верхньому
куті ігрового екрана. Тут вам знадобиться функція itemconfig.
182
РОЗДІЛ 2
ПРОГРАМУВАННЯ ЗАДАЧ
МОВОЮ PYTHON
елементи: 1, 1, 1, 1, 1, 1, ...
їхні номери: 1, 2, 3, 4, 5, 6, ...
елементи: 1, -1, 1, -1, 1, -1, ...
їхні номери: 1, 2, 3, 4, 5, 6, ...
1 1
елементи: 0, 1, 0, , 0, , ...
2 3
їхні номери: 1, 2, 3, 4, 5, 6, ...
184
Ще один спосіб задання послідовності – рекурентна формула.
Згадаємо, що це таке.
Формулу, що виражає будь-який член послідовності, почи-
наючи з деякого, через попередні (один або кілька), називають
рекурентною (від лат. recurro – повертатися).
Підсумовуючи викладене, назвемо три основні способи за-
дання послідовності.
Аналітичний – послідовність задається формулою загального
(або n-го) члена.
Рекурентний – будь-який член послідовності, починаючи з
деякого, виражається через попередні члени. При цьому способі
задання послідовності вказують її перший член або кілька поча-
ткових членів і формулу, що дозволяє визначити будь-який член
послідовності за відомими попередніми членами.
Словесний – задання послідовності описом.
Для складання програм нам частіше доведеться використову-
вати рекурентне співвідношення. Покажемо це на прикладах.
Приклад 2.1. Скласти програму виведення на екран членів
10 n
послідовності, заданої формулою an = .
n!
Для складання програми необхідно перейти від формули n-го
члена, яким задана послідовність, до рекурентного співвідно-
шення, що зв'язує попередній член з наступним, і вказати почат-
кове значення для першого або нульового елемента. Як це зро-
бити? Виявляється, дуже просто!
1. Запишемо, чому буде дорівнювати член послідовності, що
n −1
передує n-му, тобто (n-1)-й елемент послідовності: a n −1 = 10 .
( n − 1)!
2. Тепер поділимо n-й елемент на (n-1)-й, отримаємо:
10 n
an 10 n ⋅ ( n − 1)! 10
= nn!−1 = = .
an −1 10 10 n −1 ⋅ n ! n
( n − 1)!
3. Звідси виразимо, чому дорівнюватиме n-й член:
10 10
an = an−1 ⋅ , an = ⋅ an−1.
n n
185
100 1
4. Знайдемо початкове значення a 0 : a0 = = = 1, a0 = 1.
0! 1
Процедура. Процедуру, яка буде знаходити елементи послі-
довності, можна побудувати з одного циклу з параметром, у
якому кожного разу при виконанні циклу попереднє значення
буде множитися на 10 і ділитися на значення змінної циклу, як
це обумовлено n.
Мова псевдокоду
Procedure Sequence(Int k);
Int n;
Double a;
begin
a=1;
write('Шукана послідовність');
for n=1 to k do
begin
a=a*10/n;
write(a, ' ')
end;
end;
С++ Python
void Sequence(intk) def Sequence (k):
{ a=1
int n; n=1
double a = 1; print("The desired
printf("The desired sequence")
sequence\n"); for n inrange(1, k + 1):
for (n = 1; n <= k; n++) a = a * 10 / n
{ print ("%.6f" % (a))
a = a * 10 / n;
printf("%.6lf ", a);
}
}
186
Програма
Мова псевдокоду
Program Problem1; {Виведення членів послідовності}
Int k;
Procedure Create_succession(int k);
Int n;
Double a;
begin
a=1;
write(`Шукана послідовність`);
for n=1 to k do
begin
a=a*10/n;
write(a, ' ')
end;
end;
begin
write('Введіть кількість елементів'); readln(k);
Create_succession(k);
end.
С++ Python
void Sequence(intk) { def Sequence (k):
int n; a=1
double a = 1; n=1
printf("The desired print("The desired
sequence\n"); sequence")
for (n = 1; n <= k; n++) { for n inrange(1,k+1):
a = a * 10 / n; a = a * 10 / n
printf("%.6lf\n", print ("%.6f" % (a))
a);
} print("Enter the number of
} members")
int main() k = int(input())
{ Sequence(k)
int n;
printf("Enter the number
of members\n");
scanf("%d", &n);
Sequence(n);
system("pause");
}
187
Приклад 2.2. Складемо програму виведення на екран членів
послідовності десяткових наближень числа 2 .
Правило, за яким будується послідовність, указане словесно.
Треба від словесного задання перейти до рекурентної формули.
Формула для обчислення квадратних коренів. Розглянемо не-
скінченну послідовність x1, x2, x3, ..., утворену за правилом
u +1 1 u
x1 = , xi = ⋅ xi −1 + , i = 2, 3, ...
2 2 xi −1
Виявляється, що члени такої послідовності зі збільшенням номе-
ра i все менше й менше відрізняються від u (цей факт був відо-
мий Герону Олександрійському ще в І ст. н. е.). Нехай, наприклад,
u = 2. Обчислення перших членів послідовності x1, x2, x3, ... дає нам
2 +1
x1 = ≈ 1.5,
2
1 2
x2 = ⋅ 1.5 + ≈ 1.4166666,
2 1.5
1 2
x3 = ⋅ 1.4166666 + ≈ 1.4142156,
2 1.4166666
1 2
x4 = ⋅ 1.4142156 + ≈ 1.4142135.
2 1.4142156
Нагадаємо, що 2 = 1.4142135...
Використовуючи описане рекурентне співвідношення, може-
мо скласти процедуру і програму обчислення квадратних коре-
нів для будь-якого дійсного підкореневого виразу, а не тільки 2.
Процедура
Мова псевдокоду
Procedure Square_root(int n; double u);
int i;
double x;
begin
write('Десяткові наближ. з недол. кв. кореня з ', u);
x=(u+1)/2;
for i=1 to n do
begin
x=(1/2)*(x+u/x);
write(x, ' ')
end;
end;
188
С++ Python
void Square_root(intn, doubleu) def Square_root(n,u) :
{ print("decimal approximation a
int i; square root of the number", u)
double x; x=(u+1)/2;
char forma[1024]; for i inrange(1,n+1):
printf("decimal x=(1/2)*(x+u/x)
approximation a square root of the print("x =","%.{0}f".format(i)
number %lf\n", u); % x)
x = (u + 1.0) / 2;
for (i = 1; i <= n; i++)
{
x = (0.5)*(x + u / x);
sprintf(forma, "x
= %%.%if\n", i);
printf(forma, x);
}
}
Основна програма
Мова псевдокоду
Program Problem2;
int n;
double u;
Procedure Square_root(int n; double u;);
int i;
double x;
begin
write('Десяткові наближ. з недол. кв. кореня з ', u);
x=(u+1)/2;
for i=1 to n do
begin
x=(1/2)*(x+u/x);
write(x, ' ')
end;
end;
begin
write('Введіть підкореневий вираз ');
read(u);
write('Введіть кількість членів послідов. ');
read(n);
Square_root(n, u);
end.
189
С++ Python
void Square_root(intn, doubleu) def Square_root(n,u) :
{ print("decimal approximation a
int i; square root of the number", u)
double x; x=(u+1)/2;
char forma[1024]; for i inrange(1,n+1):
printf("decimal approximation a x=(1/2)*(x+u/x)
square root of the number %lf\n", print("x =","%.{0}f".format(i) %
u); x)
x = (u + 1.0) / 2;
for (i = 1; i <= n; i++) print("Enter the number ")
{ u = float(input())
x = (0.5)*(x + u / x);
sprintf(forma, print("Enter the number of
"x = %%.%if\n", i); members")
printf(forma, x); n = int(input())
} Square_root(n,u)
}
int main()
{
int n;
double u;
printf("Enter the number\n");
scanf("%lf", &u);
printf("Enter the number of
members\n");
scanf("%d", &n);
Square_root(n, u);
system("pause");
return 0;
}
Програма
Мова псевдокоду
Program Problem2a;
double u;
double x, eps;
Procedure Square_root1(double eps, double u: double pointer x);
begin
x=(u+1)/2;
repeat
x=(1/2)*(x+u/x) ;
until abs(x*x- u) <= eps
end;
191
begin
write('Введіть підкореневий вираз'); read(u);
write('Задайте точність обчислення '); read(eps);
Square_root1(eps, u, x);
write('Квадратний корінь з ', u, ' дорівнює ', x);
write(' з точністю до ', eps)
end.
С++ Python
void Square_root1(doubleeps, def Square_root(u, eps, x) :
doubleu, double *x) x[0] = (u + 1) / 2;
{ while (x[0]*x[0]-u)>eps:
*x = (u + 1.0) / 2; x[0]=(1/2)*(x[0]+u/x[0])
while (((*x)*(*x)-u)>=eps)
{ print("Enter value")
*x=(1.0/2)*((*x)+u/(*x)); u = float(input())
} print("Enter accuracy")
} eps = float(input())
a=[0];
int main() Square_root(u,eps, a)
{ print("the square root of", u, "is =",
double eps; "%.12f" % a[0])
double u;
printf("Enter value\n");
scanf("%lf", &u);
printf("Enter accuracy\n");
scanf("%lf", &eps);
double x;
Square_root1(eps, u, &x);
printf("the square root of %lf
is = %.12lf\n", u, x);
system("pause");
return 0;
}
192
Другий спосіб
Мова псевдокоду
Program Problem2b;
double u;
double x, eps;
Procedure Square_root2(double eps, double u, double pointer x);
Double x1,x2;
begin
x1=1;
x2=(1/2)*(x1+u/x1) ;
repeat
x1=x2;
x2=(1/2)*(x1+u/x1);
until abs(x2- x1) <= eps;
x=x2
end;
begin
write('Введіть підкореневий вираз'); read (u);
write('Задайте точність обчислення '); read(eps);
Square_root2(eps, u , x );
write('Квадратний корінь з ', u, ' дорівнює ', x:12:12);
write(' з точністю до ', eps:3:12)
end.
С++ Python
void Square_root1(doubleeps, def Square_root(u, eps, x) :
doubleu, double *x) x1 = 1.0
{ x2 = (1.0/2)*(x1+u/x1)
double x1, x2; while abs(x2-x1)>=eps :
x1 = 1; x1 = x2
x2 = (1.0 / 2)*(x1 + u / x1); x2 = (1.0/2)*(x1+u/x1)
do { x[0] = x2;
x1 = x2;
x2 = (1.0/2)*(x1+u / x1); print("Enter value")
}while((abs(x2-x1) >= eps)); u = float(input())
*x = x2; print("Enter accuracy")
} eps = float(input())
int main() a=[0];
{ Square_root(u,eps, a)
193
double eps; print("the square root of", u, "is =",
double u; "%.12f" % a[0])
printf("Enter value\n");
scanf("%lf", &u);
printf("Enter the accuracy\n");
scanf("%lf", &eps);
double x;
Square_root1(eps, u, &x);
printf("the square root of %lf
is = %.12lf\n", u, x);
system("pause");
return 0;
}
197
int main() a=[0];
{ Square_root(u,eps, a)
double eps; print("the square root of", u, "is =",
double u; "%.{0}f".format(t(eps)) % a[0])
printf("Enter value\n"); print("accurate to ",
scanf("%lf", &u); "%.{0}f".format(t(eps)) % eps)
printf("Enter the accuracy\n");
scanf("%lf", &eps);
double x;
Square_root(eps, u, &x);
char forma[1024];
sprintf(forma, "the square root of
%lf is = %%.%if\n", u, t(eps));
printf(forma, x);
sprintf(forma, "accurate to
%%1.%if\n", t(eps));
printf(forma, eps);
return 0;
}
Мова псевдокоду
Program Problem2b;
double u;
double x, eps;
{Функція визначення порядку:- к-ті k знаків після коми}
Function t(double eps): integer;
Int k
begin
k = -1;
repeat
eps =eps*10;
k =k+1
until eps > 1;
t =k
end;
Procedure Square_root(double eps, double u, double pointer x);
var
double x1,x2
begin
198
x1=1;
x2=(1/2)*(x1+u/x1)
repeat
x1=x2;
x2=(1/2)*(x1+u/x1)
until abs(x2- x1) <= eps;
x=x2
end;
{Основна програма}
begin
write('Введіть підкореневий вираз'); read(u);
write('Задайте точність обчислення'); read(eps);
Square_root(eps, u, x);
write('Квадратний корінь із ', u, ' дорівнює ', x:6:t(eps));
write('З точністю до ', eps:1:t(eps))
end.
С++ Python
199
double eps;
double u;
printf("Enter value\n");
scanf("%lf", &u);
printf("Enter the accuracy\n");
scanf("%lf", &eps);
double x;
Square_root(eps, u, &x);
char forma[1024];
sprintf(forma, "the square root of
%lf is = %%.%if\n", u, t(eps));
printf(forma, x);
sprintf(forma, "accurate to
%%1.%if\n", t(eps));
printf(forma, eps);
return 0;
}
Мова псевдокоду
{Обчислення кореня з будь-яким натур. показ. дійс. числа}
Program Problem3;
double u, n, eps, x;
{Функція визначення порядку k знаків після коми}
Function t(double eps): integer;
var
200
int k;
begin
k=-1;
repeat
eps=eps*10;
k=k+1
until eps > 1;
t=k
end;
Procedure Root(double u, double n, double eps, double pointer x);
double x1, x2, xn;
int i;
begin
x1=(u+n- 1)/2;
xn=1;
for i=1 to trunc(n)- 1 do xn=xn*x1;
x1=((n- 1)*x1+u/xn)/n;
repeat
x1=x2;
xn=1;
for i=1 to trunc(n)- 1 do xn=xn*x1;
x2=((n- 1)*x1+u/xn)/n
until abs(x1- x2) <= eps;
x=x2
end;
begin
write('Введіть підкореневий вираз'); read(u);
write('Введіть показник кореня '); read(n);
write('Введіть точність обчислення'); read(eps);
Root(u, n, eps, x);
write('Значення кореня дорівнює ', x:8:t(eps));
write('З точністю до ', eps:1:t(eps))
end.
С++ Python
int t(doubleeps) { frommathimport trunc
int k = -1; def t(eps) :
while (eps< 1) { k=-1
eps *= 10; whileeps< 1 :
k++; eps *= 10
} k += 1
201
return k + 1; return k+1;
}
def Root(u, eps, n, x) :
void Root(doubleeps, doubleu, xn = 1;
doublen, double *x) x1 = (u + n – 1) / 2;
{ x2 = ((n – 1)*x1 + u / xn) / n;
double x1, x2, xn; while abs(x1 – x2) >eps :
xn = 1; x1 = x2
x1 = (u + n – 1) / 2; xn = 1
x2 = ((n – 1)*x1 + u / xn) / n; for i inrange(1,trunc(n)):
do { xn *= x1
x1 = x2; x2 = ((n-1)*x1+u/xn)/n
xn = 1; x[0] = x2
for (int i=1;i<trunc(n); i++)
xn *= x1; print("Enter the root expression")
x2 = ((n-1)*x1+u / xn) / n; u = float(input())
} while (abs(x1 – x2) >eps); print("Enter the metric of root")
*x = x2; n = float(input())
} print("Enter accuracy")
eps = float(input())
int main() a=[0];
{ Root(u, eps, n, a)
double eps; print("the", n, "root of", u, "is =",
double u; "%.{0}f".format(t(eps)) % a[0])
double n; print("accurate to ",
printf("Enter the root expression\n"); "%.{0}f".format(t(eps)) % eps)
scanf("%lf", &u);
printf("Enter the metric of root\n");
scanf("%lf", &n);
printf("Enter the accuracy\n");
scanf("%lf", &eps);
double x;
Root(eps, u, n, &x);
char forma[1024];
sprintf(forma, "the %lf root of
%lf is = %%.%if\n", n, u, t(eps));
printf(forma, x);
sprintf(forma, "accurate to
%%1.%if\n", t(eps));
printf(forma, eps);
return 0;
}
202
Приклад 2.5. Послідовність задана формулою
( − 1) n −1 ⋅ x 2 n −1
un = , | x |≤ 1.
(2 n − 1)!
Вивести на екран члени послідовності до члена, меншого від
заданого додатного числа eps. Прийняти 0 < eps < 1.
Найважливіше – це перейти від формули n-го члена до реку-
рентного співвідношення, яке потім треба використовувати при
складанні програми.
Запишемо формулу (n-1)-го члена:
( −1) n − 2 ⋅ x 2 n − 3
u n −1 = .
(2 n − 3)!
Розділимо un на un-1, отримаємо:
un (−1)n−1 ⋅ x2n−1 ⋅ (2n − 3)!
= =
un−1 (−1)n−2 ⋅ x2n−3 ⋅ (2n − 1)!
(−1) ⋅ x2 ⋅ (2n − 3)! − x2
= = .
(2n − 3)!⋅ (2n − 2) ⋅ (2n − 1) (2n − 2)(2n − 1)
Звідси
x2
un = −u n −1 .
(2 n − 2)(2 n − 1)
Зазначимо, що перший член дорівнює u1 = x.
Після цього не важко скласти програму. Організуємо цикл із
передумовою "доки": доки |u| > eps. Чому для цієї задачі зручно
будувати саме такий цикл?
У нашому прикладі за початкове значення беремо перший
елемент послідовності. Може статися, що користувач задав
таке значення eps, що вже перший член менше нього. Тоді
обчислення робити не варто, тобто цикл не виконується,
отже, необхідно одразу перевірити умову. Таке роблять у ци-
клі з передумовою. Цикл repeat ... until (у Python такого
циклу немає, у С++ аналогом є do … while зі зворотною умо-
вою) буде обов'язково виконуватись хоча б один раз, а нам
цього не треба.
203
Програма
Мова псевдокоду
Program Problem4;
var
double x, eps;
{Функція визначення порядку: k знаків після коми}
Function t(double eps): integer;
Int k;
begin
k=-1;
repeat
eps=eps*10;
k=k+1
until eps > 1;
t=k
end;
Procedure Create_succession(double x, double eps);
Double u;
Int n;
begin
u=x;
n=1;
while abs(u) > eps do
begin
n=n+1;
writeln(u:6:t(eps), '; ');
u=(-1)*u*x*x/((2*n- 2)*(2*n- 1))
end;
end;
begin
write('Введіть значення |x| <= 1 '); readln(x);
write('Задайте додат. число eps '); readln(eps);
writeln('Члени послідовності');
Create_succession(x, eps);
end.
С++ Python
int t(doubleeps) { def t(eps) :
int k = -1; k=0
while (eps< 1) { whileeps< 1 :
eps *= 10; eps *= 10
k++; k += 1
204
} return k;
return k + 1;
} def Create_succession(x, eps) :
void Create_succession(doublex, u=x;
doubleeps) n=1;
{ while abs(u)>eps :
double u = x; n=n+1
int n = 1; print("%.{0}f".format(t(eps))
char forma[1024]; % u)
while (abs(u) >eps) u = -1 * u* x*x / ((2 * n – 2)*(2 * n
{ – 1))
n = n + 1; print("Enter the |X|<=1")
sprintf(forma, "%%6.%if\n", x = float(input())
t(eps)); print("Enter the EPS")
printf(forma, u); eps = float(input())
u=-1*u*x*x/((2*n-2)*(2*n-1)); print("members of the sequence")
} Create_succession(x, eps)
}
int main()
{
double eps;
double u;
printf("Enter the |X|<=1\n");
scanf("%lf", &u);
printf("Enter the EPS\n");
scanf("%lf", &eps);
printf("members of the
sequence\n");
Create_succession(u, eps);
system("pause");
return 0;
}
206
С++ Python
int t(doubleeps) { def t(eps) :
int k = 0; k=0
while (eps< 1) { whileeps< 1 :
eps *= 10; eps *= 10
k++; k += 1
} return k;
return k;
} def Create_succession(x, m, eps) :
void Create_succession(doublex, u=1;
doublem, doubleeps) n=1;
{ while abs(u)>eps :
double u = 1; u = u * (m – n + 1)*x / n
int n = 1; print("%.{0}f".format(t(eps))
char forma[1024]; % u)
while (abs(u) >eps) n += 1
{
u = u * (m – n + 1)*x / n;
print("Enter the |X|<=1")
sprintf(forma,"%%3.%if\n",t(eps)); x = float(input())
printf(forma, u); print("Enter the 0!=m!=N")
n = n + 1; m = float(input())
} print("Enter the 0<EPS<1")
} eps = float(input())
int main() print("members of the sequence")
{ Create_succession(x, m, eps)
double x;
double m;
double eps;
printf("Enter the |X|<=1\n");
scanf("%lf", &x);
printf("Enter the 0!=m!=N\n");
scanf("%lf", &m);
printf("Enter the 0<EPS<1\n");
scanf("%lf", &eps);
printf("members of the
sequence\n");
Create_succession(x, m, eps);
system("pause");
return 0;
}
207
Ускладнимо задачу та проаналізуємо такий приклад.
Приклад 2.7. Дано додатне число eps. Послідовність a1, a2,
a3,... побудована за таким правилом:
1 1 1
ai = 1 − 1 − ⋅ ... ⋅ 1 − .
2 3 i + 1
Знайти перший член an послідовності, для якого виконано умову
(an – an-1) < eps. Скласти програму виконання цього завдання.
Алгоритм
1. Початок.
Змінні та їхні типи. Змінна i набуває цілих значень:
1, 2, 3, 4, ... Змінні ap, an та eps набувають дійсних значень.
2. Основна частина.
а) Уведення значень eps.
б) Установлення початкового значення змінних i, ap та an:
i:=1, ap:=1, an:=1- 1/2.
в) Цикл "доки" з умовою: abs(an- ap) >= eps.
Команди у циклі. Значення i у циклі кожного разу має збіль-
шуватись на одиницю; значення ap:=ap*(1- 1/i); значення
an:=ap*(1- 1/(i+1)).
г) Після завершення роботи циклу – виведення значення an
на екран. Можна вивести й номер i цього члена послідовності.
3. Кінець.
Складемо процедуру та програму, використовуючи алгоритм.
Програма
Мова псевдокоду
Program Problem6;
int i;
double a, eps;
{Функція визначення порядку: k знаків після коми}
Function t(double eps): integer;
Int k;
begin
k=-1;
repeat
eps=eps*10;
k=k+1
208
until eps > 1;
t=k
end;
Procedure Create_succession(double eps; int var i, double var a);
Double an, ap;
begin
i=1; ap=1; an=1- 1/2;
while abs(an- ap) >= eps do
begin
i=i+1;
ap=an;
an=ap*(1- 1/(i+1))
end;
a=an
end;
{ Основна програма }
begin
write('Введіть будь-яке додатне число eps '); readln(eps);
Create_succession(eps, i, a);
writeln('Шуканий член послідовності ', a:6:t(eps));
writeln('Міститься на ', i, '-ому місці в послідовності')
end.
С++ Python
209
*i = *i + 1; print("Enter the 0<EPS")
ap = an; eps = float(input())
an = ap*(1- i = [0];
1.0/(*i+1)); a = [0.0];
} print("members of the sequence")
*a = an; Create_succession(eps, i , a)
} print("value =",
int main() "%.{0}f".format(t(eps)) % a[0])
{ print("in position", i[0])
double eps;
printf("Enter the
0<EPS\n");
scanf("%lf", &eps);
int i;
double a;
Create_succession(eps,
&i, &a);
char forma[1024];
sprintf(forma, "value =
%%3.%if\n", t(eps));
printf(forma, a);
printf("in %d position\n",
i);
system("pause");
return 0;
}
210
Рис. 2.1
Програма
Мова псевдокоду
Program Problem7;
longint i, n, k;
double eps, yn, yp;
{Обчислення порядку – кількості знаків після коми}
Function t(double eps): integer;
int k;
begin
k=-1;
repeat
eps=eps*10;
k=k+1
until eps > 1;
t=k
end;
{ Основна програма }
begin
211
writeln('Введіть довільне додатне число, ');
write('можна навіть дуже мале '); readln(eps);
i=1; k=-1; yn=1;
while abs(yn-2) >= eps do
begin
i=i+1;
k=k*(-1);
yn=2+k/i
end;
writeln(Умову abs(yn-2)<', eps:1:t(eps), ' задовольняє);
writeln('член послідовності yn = ', yn:6:t(eps), ',');
writeln('міститься під номером ', i);
write('Введіть номер члена послідовності більше ',i,' ');
readln(n);
if n mod 2 = 0 then yn:=2+1/n else yn:=2- 1/n;
if abs(2- yn) < eps hen
begin
write(Нерівність abs(2- ', yn:6:t(eps), ') < ', eps:1:t(eps));
writeln( ' виконується ')
end else
begin
write(Нерівність abs(2- ', yn:6:t(eps), ') < ', eps:1:t(eps));
writeln(' не виконується')
end
end.
С++ Python
int t(doubleeps) { def t(eps) :
int k = 0; k=0
while (eps< 1) { whileeps< 1 :
eps *= 10; eps *= 10
k++; k += 1
} return k; return k;
}
int main() yn = 1
{ k = -1
double eps, yn = 1, k = -1; i=1
long i = 1, y = 1; y=1
printf("Enter the 0<EPS\n"); print("Enter the 0<EPS")
scanf("%lf", &eps); eps = float(input())
212
while (abs(yn – 2) >= eps) while abs(yn – 2) >= eps :
{ i = i + 1;
i = i + 1; k = k * -1;
k = k * -1; yn = 2 + k / i;
yn = 2 + k / i; print("abs(yn-2) <",
} "%1.{0}f".format(t(eps)) % eps)
char forma[1024]; print("yn =",
sprintf(forma, "abs(yn-2) < "%6.{0}f".format(t(eps)) % yn)
%%1.%if\n", t(eps)); print("in position", i)
printf(forma, eps); print("Enter n>position\n");
sprintf(forma,"yn = n = int(input())
%%6.%if\n", t(eps)); if n%2 == 0 :
printf(forma, yn); yn = 2 + 1.0 / n
printf("in %d position\n", i); else :
printf("Enter n>position\n"); yn = 2 – 1.0 / n;
long n; print("Inequality abs(2 -%6.{0}f <
scanf("%d", &n); ".format(t(eps)) %
if (n % 2 == 0) yn,"%1.{0}f)".format(t(eps)) % eps);
yn = 2 + 1.0 / n; if abs(2 – yn) < eps :
else print("TRUE!\n")
yn = 2 – 1.0 / n; else:
sprintf(forma, "Inequality print("FALSE!\n")
abs(2-%%6.%if)<%%1.%if\n",
t(eps), t(eps));
printf(forma, yn, eps);
if (abs(2 – yn) < eps)
printf("TRUE!\n");
else
printf("FALSE!\n");
system("pause");
return 0;
}
213
Перед виконанням завдання звернемося до математичного
аналізу, у якому є теорема, що належить чеському математику
Больцано і французькому математику Коші, її часто називають
принципом збіжності. Вона висловлює загальну ознаку існуван-
ня кінцевої межі послідовності.
Нехай задана послідовність xn, що пробігає значення x1, x2, ..., xn,
..., xm, ... Для того щоб послідовність xn мала кінцеву межу, необ-
хідно й достатньо, щоб для будь-якого числа ε > 0 існував такий
номер N, щоб нерівність ( xn − xm ) < ε виконувалася для всіх
n > N та m > N . Суть полягає в тому, щоб значення змінних xn
та xm між собою безмежно зближалися при зростанні їхніх номерів.
Ця ознака допускає просту геометричну ілюстрацію (рис. 2.2).
Рис. 2.2
214
пеня зі зміною номера n. Досвід складання підпрограм у нас
уже є: на минулих заняттях ми складали багато підпрограм у
формі процедур і функцій.
Основна програма. Спочатку попросимо користувача задати
точність обчислення (так ми назвали число eps).
Потім задається початкове значення змінної n (n: = 1). Орга-
нізовується цикл з умовою (repeat ... until ...), у якому змінній x –
члену послідовності – присвоюється значення, що обчислює
значення функції x = s ( n, (1 + 1 / n)) для даного значення n. Далі
значення n збільшується на 1 ( n := n + 1) і обчислюється на-
ступний член послідовності x1, який також буде дорівнювати
значенню функції x1 = s ( n , (1 + 1 / n )) (пам'ятайте, що зна-
чення n уже збільшилося на 1).
Перевіряємо умову abs (x1-x) < eps. Як тільки умова викона-
ється, цикл закінчиться. Як значення межі послідовності ми мо-
жемо брати будь-яке зі значень x або x1, воно вкаже значення
межі послідовності із заданою точністю eps.
Якщо хто-небудь із вас раніше зустрічався з такою послідов-
ністю, то він, скоріш за все, був упевнений, що її межа існує й
дорівнює числу e – основі натурального логарифма.
Процедура
Мова псевдокоду
Function s(int k; double p): double;
Int i;
double z;
begin
z=1;
for i=1 to k do z=z*p;
s=z
end;
С++ Python
double s(intk, doublep) { def s(k, p) :
double z = 1; z = 1;
for (int i = 1; i <= k; i++) for i inrange(1,k+1) :
z *= p; z *= p;
return z; return z;
}
215
Програма
Мова псевдокоду
Program Problem8;
int n;
double x, x1, eps;
{ Функція обчислення порядку k значень після коми}
Function t(double eps): int;
int k;
begin
k=-1;
repeat
eps=eps*10;
k=k+1
until eps > 1;
t=k
end;
Function s(int k; double p): double;
int i;
double z;
begin
z=1;
for i=1 to k do z=z*p;
s=z
end;
begin
write('Задайте точність '); readln(eps);
n=1;
repeat
x=s(n, (1+1/n));
n=n+1;
x1=s(n, (1+1/n))
until abs(x1- x) < eps;
writeln('Межа послідовності дорівнює ', x1:6:t(eps));
writeln('З точністю до ', eps:1:t(eps))
end.
С++ Python
int t(doubleeps) { def t(eps) :
int k = 0; k=0
while (eps< 1) { while eps< 1 :
eps *= 10; eps *= 10
k++; k += 1
216
} return k;
return k;
} def s(k, p) :
double s(intk, doublep) { z = 1;
double z = 1; for i inrange(1,k+1) :
for (int i = 1; i <= k; i++) z *= p;
z *= p; return z;
return z; n = 1;
} print("Enter the 0<EPS");
int main() eps = float(input())
{ while True:
double eps, x, x1; x = s(n, (1 + 1 / n))
int n = 1; n=n+1
printf("Enter the 0<EPS\n"); x1 = s(n, (1 + 1 / n))
scanf("%lf", &eps); if abs(x1-x) < eps :
do { break;
x = s(n, (1 + 1.0 / n));
n = n + 1; print("limit of sequense is
x1 = s(n, (1 + 1.0 %6.{0}f".format(t(eps)) % x1)
/ n)); print("accurate to
} while (abs(x1 – x) > eps); %1.{0}f".format(t(eps)) % eps)
char forma[1024];
sprintf(forma, "limit of
sequense is %%6.%if\n", t(eps));
printf(forma, x1);
sprintf(forma, "accurate to
%%1.%if\n", t(eps));
printf(forma, eps);
system("pause");
return 0;
}
Вправи
1. Дано дійсне число b > 0. Послідовність a1, a2, ... побудо-
вана за таким правилом: a1 = 1, ai = ai2−1 + 1 (i = 2, 3, ...). Треба
отримати всі a1, a2, ..., які менше чи дорівнюють b.
2. Дано дійсне число b > 0. Послідовність a1, a2,... побудова-
1
на за правилом: a1 = b, ai = ai −1 − , i = 2, 3, ... . Знайти пе-
i
рший від'ємний член послідовності a1, a2, ... .
217
3. Скласти програму обчислення й виведення на екран n чле-
нів послідовності, заданої формулою n-го члена. Попередньо
скласти рекурентну формулу.
(n + 2)! 2n −1 n2 2n − 1
а) an = n
; б) an = n +1
; в) a n = n
; г) an = n ;
3 3 3 3 ⋅ n!
n n2 n xn
д) an = 3 n ; е) an = ; є) an = 2 4 ; ж) an = ,
n⋅2 n! n n!
де x – задане дійсне число, x > 1;
3
з) an = ( −1) n +1 n n .
2
4. Скласти програму, що підраховує суму n перших членів
послідовності, заданої формулою n-го члена. Попередньо склас-
ти рекурентну формулу.
xn n
а) an = 10n ⋅ xn ; б) an = (−1)n +1 ⋅ ; в) an = x n −1 ;
n n ⋅10
г) an = n!⋅ xn ; д) an = 2n−1 ⋅ x2(n−1) ;
x 2 n −1 n−1 n−1
е) an = ( −1) n +1 ; є) an = (n − 1) ⋅ 3 ⋅ x ;
(2 n − 1) ⋅ (2 n − 1)!
xn ( nx ) n ln(n + 1) n+1
ж) an = ; з) an = ; и) an = ⋅x ,
n ( n + 1) n! n +1
де x – задане дійсне число.
5. Дано дійсне b < 0. Послідовність a1 , a2, ... побудована за
a i −1 + 1
таким правилом: a1 = b, ai = , i = 2, 3, ... .
i ⋅ sin 2 i
Знайти перший невід'ємний член послідовності.
6. Фішка може рухатися по полю довжиною N тільки вперед.
Довжина ходу фішки не більше K. Знайти кількість різних шля-
хів, якими фішка може пройти поле від початку до кінця.
Приклад. N=3, K=2. Можливі шляхи:
1,1,1
1,2
2,1
218
7. Покупець має купюри номіналом A(1), ..., A(n), а продавець
– B(1), ... , B(m). Необхідно знайти максимальну вартість товару
Р, яку покупець не зможе оплатити, оскільки не матиме змоги
точно розрахуватися із продавцем, хоча грошей на покупку то-
вару в нього достатньо.
8. Покупець має n монет номіналом H(1), ... , H(n). Продавець
має m монет номіналом B(1), ... , B(l). Чи зможе покупець купити
річ вартістю S так, щоб у продавця знайшлась точна решта (як-
що вона необхідна).
9. Дано натуральне n. Обчислити:
1 1 1
1 + 2 1 + 2 ⋅ ... ⋅ 1 + 2 .
1 2 n
10. Дано опуклий n-кутник, n=>3. Розбити його на трикутни-
ки (n-3) діагоналями, що перетинаються лише на кінцях, таким
чином, щоб:
а) cума їхніх довжин була мінімальною;
б) максимальна з діагоналей мала найменшу довжину.
11. Для чисел Фібоначчі u0, u1, ... справедлива формула Біне:
k k
1 1 + 5 1 1 − 5
uk = ⋅ − ⋅ , где k = 0, 1, ... .
5 2 5 2
1− 5
Оскільки < 1, то для великих k виконано наближену
2
k
1 1+ 5
рівність uk = ⋅ . Обчислити й округлити до най-
5 2
ближчого цілого усі числа
k
1 1+ 5
⋅ (k = 0, 1, ..., 15),
5 2
а також обчислити u0, u1, ..., u15 за формулами та порівняти
u 0 = 0; u1 = 1; u k = u k −1 + u k − 2 ( k = 2, 3, ...), потім порів-
няти результати.
219
12. З послідовності, що складається із N чисел, викреслити
мінімальну кількість елементів так, щоб ті, що залишаться,
створили строго зростаючу послідовність.
13. Обчислити й вивести на екран додатні значення функції
n
y = sin( nx ) − cos • р Џ n = 1, 2, ..., 50.
x
14. Нехай x = a1, a2,..., am) та y = (b1, b2,..., bn) – два задані
рядки символів. Визначимо d(x, y) як мінімальну кількість вста-
вок, видалень і замін символу, яка необхідна для перетворення
їx на y. Наприклад: d(ptslddf, tsgldds) = 3.
2.2. Рекурсія
Поговорити про рекурсію детальніше.
Рекурсія – це спосіб організації підпрограми, при якому в
процесі виконання вона звертається сама до себе.
Перш за все, треба дати рекурсивне визначення постановці
завдання. Воно складається з умови закінчення рекурсії та влас-
не тіла рекурсії.
221
Приклад 2.10. Розглянемо задачу обчислення суми чисел.
При стандартному підході ми рахували суму таким чином: до
деякої змінної (початкове значення якої дорівнює нулю) додавали
значення першого числа, до результату – значення другого; про-
цес продовжували доти, поки не вичерпували всю кількість чисел.
Отже, визначення процесу підсумовування може бути таке:
до поточного значення суми додати наступне число зі списку,
поки не вичерпається список.
У межах рекурсивного підходу ми по-іншому будуємо обчи-
слювальний процес. Припустимо, що нам відома сума попере-
дніх чисел. Тоді для того щоб знайти суму чисел, треба до відо-
мої суми додати останнє число, що залишилося. Звідси маємо
таке рекурсивне визначення процесу підсумовування:
• Якщо число одне, то сума дорівнює цьому числу.
• В іншому випадку сума дорівнює: останнє число зі списку
додати до суми попередніх чисел.
Зверніть увагу, що в другому пункті можна до останнього
числа додавати суму чисел, що залишилися, а до першого – зі
списку підсумовування.
Сформулюємо визначення формальніше. Нехай n – кількість
чисел для підсумовування, Sum (n) – ім'я процесу підсумовуван-
ня n чисел, x – поточне число із заданої множини для підсумо-
вування. Тоді визначення буде таким:
Якщо n = 1 то Ввести число x і Sum: = x.
Інакше Ввести число x і Sum: = x + Sum (n-1)
Слід одразу сказати, що коли написане рекурсивне визначен-
ня, написання програми мовою Python не становитиме жодних
труднощів: просто кожен пункт визначення замінимо відповід-
ним оператором і додамо опис змінних.
Псевдокод
program demo_rek1;
function sum(n:byte):byte;
var x:byte;
begin
if n=1 then
begin
read(x);
sum:=x
end else
222
begin
read(x);
sum:=x+sum(n-1);
end;
end;
begin
read(n);
write('sum=',sum(n));
end.
С++
int sum(intn)
{
int x;
if (n == 1)
{
scanf("%d",&x);
return x;
}
else
{
scanf("%d", &x);
return x + sum(n – 1);
}
}
int main()
{
int n;
scanf("%d", &n);
printf("sum=%d\n", sum(n));
return 0;
}
Python
def sum(n):
if (n == 1):
x = int(input())
return x
else :
x = int(input())
return x + sum(n – 1)
a = int(input())
print("sum =", sum(a))
223
Якщо з клавіатури вводиться п'ять чисел: 4 5 4 3 2, то про-
грама надрукує результат 14. Перевірте.
Розглянемо ще один приклад – друкування перших натура-
льних чисел для демонстрації прямого та зворотного ходу реку-
рсії. Нехай програма має вигляд:
Псевдокод
program demo_rek2;
procedure druk(n:byte);
begin
if n=1 then write(n)
else
begin
write(n);
druk(n-1);
write(n);
end;
end;
begin
druk(10);
end.
С++
void druk(intn)
{
if (n == 1)
printf("%d", n);
else
{
printf("%d", n);
druk(n – 1);
printf("%d", n);
}
}
int main()
{
druk(10);
printf("\n");
return 0;
}
Python
def druk(n):
global s
224
if (n == 1):
s+=str(n)
else :
s+=str(n)
druk(n – 1)
s+=str(n)
s=""
druk(10)
print(s)
225
Зобразимо цей процес схематично. Позначимо через внутрішню
змінну рекурсії nі виклик процедури druk. Отримаємо (рис. 2.3):
Рис. 2.3
227
Рис. 2.4
Псевдокод
program iterat_reсurs;
var n:integer;
procedure reсursion (i:integer);
begin
writeln(i);
if i > 1 then reсursion(i-1);
end; (* Рекурсія *)
procedure iterat(i:integer);
var k,m:integer;
begin
k:=1; m:=i;
while m >= 1 do
228
begin
write(m);
m:=m-1;
end;
while k <= i do
begin
write(k);
k:=k+1;
end;
end; (* Цикл *)
begin
write('Уведіть n:'); readln(n);
writeln('Поки:');
iterat(n);
writeln;
writeln('Рекурсія');
reсursion(n);
end.
C++ Python
void reсursion(inti) def reсursion(i):
{ global s
if (i> 1) if i > 1:
reсursion(i – 1); reсursion(i – 1)
printf("%d", i); s+=str(i)
}
void iterat(inti) def iterat(i):
{ global s
int k = 1; k=1
while (k <= i) while (k <= i):
{ s+=str(k)
printf("%d", k); k += 1
k += 1; print("Уведіть n : ")
} n = int(input())
} s="Поки:\n"
int main() iterat(n)
{ print(s)
setlocale(LC_ALL, "rus"); s="Рекурсія\n"
int n; reсursion(n)
printf("Уведіть n : "); print(s)
scanf("%d", &n);
229
printf("Поки:\n");
iterat(n);
printf("\nРекурсія\n");
reсursion(n);
printf("\n");
system("pause");
return 0;
}
Псевдокод
const n=4;
function fac(n: byte):byte;
begin
if (n = 0) or (n = 1)
then fac:=1
else fac:=n*fac(n-1)
end;
begin
writeln('fac=',fac(n));
end.
230
C++
int fac(intn)
{
if ((n == 0) || (n == 1))
return 1;
else
returnn * fac(n – 1);
}
int main()
{
constint n = 4;
printf("fac = %d", fac(n));
return 0;
}
Python
def fac(n):
if (n == 0) | (n == 1):
return 1
else :
returnn * fac(n – 1)
n=4
print("fac =", fac(n))
231
Значення n зменшиться на 1 і дорівнюватиме 2 і т. д. до тих
пір, поки n не стане рівним 1, при цьому fac отримає значення 1.
Потім почнеться зворотний процес. Керування кожного разу
після завершення поточного виклику процедури буде передава-
тися оператору fac: = n * fac (n – 1). Весь зворотний процес від-
буватиметься так:
fac:=2*1; fac:=3*2; f:=6*4.
Образно кажучи, при прямому процесі значення n від 4 до 1
запам'ятовуються в стекову пам'ять, а при зворотному – значен-
ня n зчитуються зі стекової пам'яті та множаться на значення
fac. Схематично це можна зобразити так: значення n запам'ято-
вуються в стек-пам'ять:
4
3 4
2 3 4
1 2 3 4
232
Отже, при кожному входженні в рекурсивну підпрограму її
локальні змінні розміщуються в особливим чином організованій
області пам'яті – програмному стеці.
Рекурсивна форма організації підпрограми зазвичай має ви-
тонченіший вигляд порівняно з послідовною й дає компактні-
ший текст програми, але при виконанні зазвичай повільніша та
може викликати переповнення стека.
Як ми вже зазначали, при виклику функцій відбувається ро-
бота зі спеціальною пам'яттю – стеком. Обчислимо, скільки ра-
зів можна записувати в неї значення, не викликавши ситуацію
переповнення.
Приклад 2.14. Нескінченна рекурсія, за допомогою якої мо-
жна встановити, наскільки великий стек. Програма буде пере-
рвана з видачею повідомлення про помилку "Error: stack
overflow error (" Помилка: переповнення стека ").
Псевдокод
Programstack_test;
{програма перевірки стека}
procedure proc(i:integer);
begin
writeln(i);
proc(i+1);
end;
begin
proc(1);
end.
С++ Python
void proc(inti) def proc(i):
{ print(i)
printf("%d\n", i); proc(i + 1)
proc(i + 1); proc(1)
}
int main()
{
proc(1);
return 0;
}
Процедура
Псевдокод
Procedure uncle(k, p, s, n: longint); {uncle – дядько}
begin
if n = 1
then write(s)
else
begin
k:=k+1;
p:=2*p+k;
uncle(k, p, s+p, n- 1)
end
end;
С++
void uncle(longintk, longintp, longints, intn)
{
if (n == 1)
printf("%ld", s);
else
234
{
k = k + 1;
p = 2 * p + k;
uncle(k, p, s + p, n – 1);
}
}
Python
def uncle(k, p, s, n):
if n == 1 :
print(s)
else :
k=k+1
p=2*p+k
uncle(k, p, s + p, n – 1)
Програма
Псевдокод
Program Rich_man1; { richman – багатий }
var
n: integer;
Procedure uncle(k, p, s, n: longint); {uncle – дядько}
begin
if n = 1
then write(s)
else
begin
k:=k+1;
p:=2*p+k;
235
uncle(k, p, s+p, n- 1)
end
end;
begin
write('Уведіть кількість років племінника '); readln(n);
write('Я отримаю к ', n, '-му дню народження ');
uncle(1, 1, 1, n);
writeln(' доларів')
end.
С++
void uncle(longintk, longintp, longints, intn)
{
if (n == 1)
printf("%ld", s);
else
{
k = k + 1;
p = 2 * p + k;
uncle(k, p, s + p, n – 1);
}
}
int main()
{
int n;
printf("Уведіть кількість років племінника "); scanf("%d",
&n);
printf("Я отримаю к %d-му дню народження ", n);
uncle(1, 1, 1, n);
printf(" доларів\n");
system("pause");
return 0;
}
Python
def uncle(k, p, s, n):
ifn == 1 :
print(s," доларів")
else :
k=k+1
p=2*p+k
uncle(k, p, s + p, n – 1)
236
print("Уведіть кількість років племінника "); n = int(input())
print("Я отримаю к ", n, "-му дню народження ")
uncle(1, 1, 1, n)
Псевдокод
Program Rich_man2;
Var n: integer;
Procedure uncle1(k, p, s, n: longint);
begin
if s >= 100
then write(n)
else
begin
k:=k+1;
p:=2*p+k;
uncle1(k, p, s+p, n+1)
end
end;
begin
write('Сума подарунка перебільшить 100 доларів к ');
uncle1(1, 1, 1, 1);
writeln('-му дню народження')
end.
С++
void uncle(longintk, longintp, longints, intn)
{
if (s>= 100)
printf("%d", n);
else
{
k = k + 1;
p = 2 * p + k;
uncle(k, p, s + p, n + 1);
}
237
}
intmain()
{
printf("Сума подарунка перебільшить 100 доларів к ");
uncle(1, 1, 1, 1);
printf("-му дню народження'\n")
return 0;
}
Python
def uncle(k, p, s, n):
ifs>= 100 :
print(n,"-му дню народження")
else :
k=k+1
p=2*p+k
uncle(k, p, s + p, n + 1)
print("Сума подарунка перебільшить 100 доларів к ");
uncle(1, 1, 1, 1)
Псевдокод
Programdezimal_oktal_konvertierung;
{переведення з десяткової системи у восьмеричну}
var z:integer;
procedure convert(z:integer);
begin
if z > 1 then convert(z div 8);
write(z mod 8:1);
end;
begin
writeln('Уведіть деяке натуральне число:'); readln(z);
writeln('Десяткове число:',z:6);
write('Восьмеричне число: ');
convert(z);
end.
238
С++
void convert(intz)
{
if( z> 1)
convert(z / 8);
printf("%d", z % 8);
}
intmain()
{
intz;
printf("Уведіть деяке натуральне число: "); scanf("%d", &z);
printf("Десяткове число : %6d\n", z);
printf("Восьмеричне число : ");
convert(z);
printf("\n");
return 0;
}
Python
def convert(z):
global s
if z> 1:
convert(int(z / 8))
s += str(z % 8)
239
Процедура multiplication множить число a на кожну цифру чи-
сла b, починаючи з правої цифри. Остання цифра отриманого до-
бутку, складена із останньою цифрою наявного в пам'яті частко-
вого добутку, друкується, а всі інші цифри запамятовуються й пе-
редаються як параметри при рекурсивному зверненні до процеду-
ри multiplication. Наприкінці виконується множення на першу (лі-
ву) цифру числа b. На цьому множення закінчується. Тоді друку-
ється початок результату – накопичений частковий добуток без
останньої цифри (s div 10), а після нього, при поверненні з рекурсії,
– усі інші цифри добутку (s mod 10) у зворотному порядку порів-
няно з тим, як вони обчислювалися при входженні в рекурсію.
Процедура
Псевдокод
Procedure multiplication(a, b, s: longint);
begin
if b <> 0 then
begin
s:=s+a*(b mod 10);
multiplication(a, b div 10, s div 10);
write(s mod 10:1)
end
else if s <> 0 then write(s)
end;
C++
void multiplication(longinta, longintb, longints)
{
if (b != 0)
{
s = s + a * (b % 10);
multiplication(a, b / 10, s / 10);
printf("%ld", s % 10);
}
elseif (s != 0)
printf("%ld", s);
}
Python
def multiplication(a, b, s):
ifb != 0 :
240
s = s + a * (b % 10)
multiplication(a, int(b / 10), int(s / 10))
print(s % 10)
elifs != 0 :
print(s)
Програма
Псевдокод
Program Problem13; {Великий добуток}
var
x, y: longint;
Procedure multiplication(a, b, s: longint);
begin
if b <> 0 then
begin
s:=s+a*(b mod 10);
multiplication(a, b div 10, s div 10);
write(s mod 10:1)
end else if s <> 0 then write(s)
end;
begin
write('Уведіть перший множник '); readln(x);
write('Уведіть другий множник '); readln(y);
write(x,'*',y:1,' = ');
if ((x < 0) and (y > 0)) or ((x > 0) and (y < 0)) then write('-');
multiplication(abs(x), abs(y), 0);
end.
С++
void multiplication(longinta, longintb, longints)
{
if (b != 0)
{
s = s + a * (b % 10);
multiplication(a, b / 10, s / 10);
printf("%ld", s % 10);
}
elseif (s != 0)
printf("%ld", s);
}
241
int main()
{
longint x, y;
printf("Уведіть перший множник "); scanf("%ld", &x);
printf("Уведіть другий множник "); scanf("%ld", &y);
printf("%d * %ld = ", x, y);
if (((x < 0) && (y > 0)) || ((x > 0) && (y < 0)))
printf("-");
multiplication(abs(x), abs(y), 0);
printf("\n");
return 0;
}
Python
def multiplication(a, b, s):
global result
ifb != 0 :
s = s + a * (b % 10)
multiplication(a, int(b / 10), int(s / 10))
result+=str(s%10)
elif s != 0 :
result+=str(s)
print("Уведіть перший множник ")
x=int(input())
print("Уведіть другий множник ")
y=int(input())
result=""
if ((x < 0) & (y > 0)) | ((x > 0) & (y < 0)):
result+="-"
multiplication(abs(x), abs(y), 0);
print(x, "*", y, "=", result);
242
Псевдокод
Procedure A(i: integer);
begin
...
B(i);
...
end;
Procedure B(j: integer);
...
begin
...
A(j);
...
end;
С++ Python
void A(inti) def A(i):
{ ...
... B(i);
B(i); ...
... def B(i):
} A(i);
void B(inti)
{
A(i);
}
243
Псевдокод
procedure SPIRAL1(a); procedure SPIRAL2(a);
begin begin
if a>10 then if a>10 then
begin begin
d(a); SPIRAL2(a-10);
rt(90); fd(a);
SPIRAL1(a-10); rt(90);
end end
end; end;
С++
void SPIRAL1(inta)
void SPIRAL2(inta)
{
{
if (a> 10){
if (a> 10){ SPIRAL2(a – 10);
fd(a); rt(90); SPIRAL1(a –
fd(a); rt(90);
10);
}
}
}
}
Python
def SPIRAL1(a): def SPIRAL2(a):
ifa> 10: ifa> 10:SPIRAL2(a-10)
fd(a) fd(a)
rt(90)SPIRAL1(a – 10) rt(90)
244
2.5. Швидке піднесення до степеня
за допомогою рекурсії
Розглянемо ще один приклад. Нехай треба піднести число до
цілого степеня. У найпримітивнішому варіанті його можна прос-
то помножити само на себе потрібну кількість разів, але це – най-
повільніший зі способів. Ми скористаємося іншою ідеєю і побу-
дуємо рекурсивне визначення операції піднесення до степеня.
0. Якщо степінь дорівнює 0, то результат 1.
1. Якщо степінь, до якого треба піднести число, парний, то
піднесемо його до квадрата, а квадрат – до степеня вдвічі менше
первинного.
2. Якщо степінь, до якого потрібно піднести число, непарний,
то піднесемо число до парного степеня, який на одиницю мен-
ший ніж необхідний, способом, наведеним у кроці 1, а потім
просто помножимо результат на число.
При такому підході необхідна кількість множень скорочуєть-
ся вдвічі. Якщо ж організувати за цією схемою циклічні обчис-
лення, використовуючи її для піднесення квадрата числа до сте-
пеня, то ми досягнемо теоретично мінімальної кількості мно-
жень, можливої для цього завдання.
Наведемо тепер формальний алгоритм, а потім подивимося,
який елегантний вигляд усе матиме в рекурсивному стилі:
1. Умова виходу.
2. Якщо n парне, то.
3. Якщо n непарне, то.
Реалізація цього алгоритму мовами C++ та Python просто до-
слівно його повторює:
Псевдокод
function power(a:longint;x:word):longint;
begin
if x=0 then power:=1
else if x mod 2 = 0 then power:=power(sqr(a),x div 2)
else power:=a*power(sqr(a),(x-1) div 2);
end;
С++
longint power(longinta, unsignedintx)
{
if (x == 0) return 1;
245
elseif (x % 2 == 0) return power(a * a, x / 2);
elsereturna * power(a * a, (x – 1) / 2);
}
Python
def power(a, x):
if x == 0: return 1
elif: x % 2 == 0: return power(a * a, x / 2)
else: returna * power(a * a, (x – 1) / 2)
246
Перенести стос із n-1 диска зі шпиля c на шпиль b, викорис-
товуючи як допоміжний шпиль a.
Рекурсивність визначення полягає в тому, що для перенесен-
ня n-1 диска на тимчасовий шпиль і з нього використовуються ті
самі визначення, тільки кількість дисків зменшується з кожним
перенесенням стосу. Отже, досягнемо значення n = 1, і рекурсія
зупиниться.
Тепер, припускаючи, що всі диски лежать правильно, тобто
менший на більшому, і що інші диски на всіх трьох шпилях не
менше верхніх m на шпилі а, легко скласти програму (ми вже
знаємо, що, написавши рекурсивне визначення завдання, напи-
сати програму не становить труднощів).
Нехай Р (m, а, b, с) – процедура, яка переносить m дисків зі
шпиля a на шпиль b, використовуючи як допоміжний шпиль c.
Для програмування позначимо шпилі а, b, с цифрами 0, 1, 2 і
будемо друкувати потрібні перенесення дисків. Перенесення зі
шпиля а на b зображатимемо як 0 → 1, з b на c – як 1 → 2 і т. д.
Отримаємо таку програму:
Псевдокод
programHANOJ;
var n: integer;
procedure P(m, a, b, c: integer);
begin
if m=1 then writeln(a,' → ', b,' ') {переносимо один}
else
begin
P(m-1, a, c, b); {переносимо стос із m-1 верхніх
дисків}
P(1, a, b, c); {переносимо один найбільший}
P(m-1, c, b, a) {переносимо стос із m-1 верхніх дис-
ків}
end
end;
begin
readln (n); writeln('n =', n);
P(n, 0, 1,2)
end.
247
C++
void P(intm, inta, intb, intc)
{
if (m == 1)
printf("%d -> %d\n", a, b); // переносимо один
else
{
P(m – 1, a, c, b);//переносимо стос із m – 1 верхніх
дисків
P(1, a, b, c);//переносимо один найбільший
P(m – 1, c, b, a);//переносимо стос із m – 1 верхніх
дисків
}
}
int main()
{
int n; scanf("%d", &n);
printf("n = %d\n", n);
P(n, 0, 1, 2);
return 0;
}
Python
def P(m, a, b, c):
global s
if (m == 1):
s+=str(a)+" -> "+str(b)+" "
else:
P(m – 1, a, c, b)
P(1, a, b, c);
P(m – 1, c, b, a)
n = int(input())
s = "n = "+str(n)+"\n"
P(n, 0, 1, 2)
print(s)
248
2.7. Нерекурсивний метод
Для того щоб реалізувати ітераційне розв'язання, досить
отримати формули, що визначають вихідний і приймальний
стрижні залежно від номера кроку. Простий аналіз показує, що
рекурсивні виклики відрізняються один від одного значеннями
параметрів, що визначають кількість оброблюваних дисків, і
шпилями, використовуваними для перенесення на кожному
кроці. А що станеться, якщо вдасться позбутися цих параметрів?
Тоді рекурсивне розв'язання запишемо таким чином:
Побудувати вежу =
побудувати вежу
Перенести диск з одного стрижня на інший
Побудувати вежу.
Отже, є три хвостові рекурсії, які легко зводяться до ітерацій:
Побудувати вежу =
Задану кількість разів, а саме: 2 N − 1 перенести диск з одно-
го стрижня на інший.
Подібні прийоми широко використовуються при розробці
трансляторів і детально описані.
Шпилю, на якому диски містяться спочатку, дамо номер 0;
шпилю, на який їх треба перенести – номер 2; відповідно остан-
ньому шпилю – номер 1.
Нехай усього дисків N. Пронумеруємо їх у порядку збіль-
шення радіуса числами 0, 1, 2, ..., 2 N − 1.
Будь-яке натуральне число i зобразимо у вигляді
i = (2t + 1)*2k , де t і k – цілі числа (тобто як добуток непарного
числа на деякий степінь двійки). На i-му ході переноситься диск
із номером k зі шпиля з номером (−1) N −k * t mod3 на шпиль із
N −k
номером (−1) *(t + 1)mod3 .
Приклад для N = 4.
Хід Диск t Зі Hа
шпиля шпиль
1 0 0 0 1
2 1 0 0 2
249
3 0 1 1 2
4 2 0 0 1
5 0 2 2 0
6 1 1 2 1
7 0 3 0 1
8 3 0 0 1
9 0 4 1 2
10 1 2 1 0
11 0 5 2 0
12 2 1 1 2
13 0 6 0 1
14 1 3 0 2
15 0 7 1 2
Якщо уявити, що шпилі, на які одягаються диски, розташо-
вані в кутах рівностороннього трикутника, то найменший диск
при кожному непарному ході рухається за (або проти, що зале-
жить від початкової кількості дисків) годинниковою стрілкою.
Усі парні ходи визначаються однозначно: спочатку перекла-
дається менший диск, оскільки не можна чіпати диск 0 і класти
більший на менший.
Зазначимо дві закономірності:
1) На кожному непарному ході відбувається перенесення
найменшого диска.
2) Найменший диск завжди переноситься циклічно: або
A → B → C → A → B → C → ...
(у разі парної кількості дисків), або
A → C → B → A → C → B → ...
(у разі непарної).
Маємо алгоритм:
1. Визначаємо кількість дисків, тобто як буде переміщатися най-
менший диск (цей крок виконується на початку, до того ж один раз).
2. Зазначаємо номер ходу: якщо непарний – переносимо най-
менший диск у напрямку, визначеному в п. 1; якщо парний – то
можливий один хід – беремо найменший із двох верхніх дисків і
переносимо його.
Можна написати трохи по-іншому.
250
Для N від 1 до 2k − 1 виконувати:
1. У двійковому поданні N знайти найправіший ненульовий
розряд. Позначимо його номер t.
2. Позначимо номер шпиля, на якому міститься диск t, через
t
i. Перемістити диск t зі шпиля i на шпиль (i + (−1) )mod3 .
До речі, за номером ходу легко можна відновити положення
дисків на шпилях: після i-го ходу диск із номером j міститься на
n− j j j +1
шпилі з номером (−1) ((i div 2 ) − (i div 2 )) mod 3 .
У загальному випадку рекурсія може бути успішно застосована
для обробки об'єктів, частини яких мають той самий тип, що й сам
об'єкт (будь-яка частина дерева – дерево, будь-яка частина масиву –
масив тощо). Слід звертати особливу увагу на умову виходу: якщо
вона задана некоректно, то підпрограма зациклиться. Інша важлива
властивість – глибина рекурсії, тобто кількість викликів підпрогра-
мою самої себе до спрацьовування умови виходу. Не передавайте
рекурсивним процедурам громіздкі об'єкти (масиви тощо). Для об-
робки таких структур або оголошуйте їх глобально, або розміщуйте
у пам'яті динамічно, а покажчик передавайте на масив.
Псевдокод
Function Func (var x: Integer): Integer;
begin
if x<0 then x:=0
else Func:=x+10
end;
var i: integer;
begin
251
i:=1;
i:=2*Func(i)-100; {Стандартний виклик функції}
Func(i) {Розширений синтаксис виклику}
end.
С++
int Func(int *x)
{
if (x< 0) *x = 0;
elsereturn *x + 10;
}
int main()
{
int i;
i = 1;
i = 2 * Func(&i) – 100; // Стандартний виклик функції
Func(&i); // Розширений синтаксис виклику
return 0;
}
Python
def Func(x):
if x[0] < 0:
x[0] = 0
else:
returnx[0] + 10
i = [1]
i[0] = 2 * Func(i) – 100; # Стандартний виклик функції
Func(i) # Розширений синтаксис виклику
Вправи
1. Нарисувати такі фігури:
252
Вказівка. Дати рекурсивне визначення: наприклад, це прави-
льний шестикутник, на кожній стороні якого нарисований пра-
вильний шестикутник, на кожній стороні якого нарисований
правильний шестикутник і т. д.
2. Написати рекурсивні функції Fact (N) і Fact2 (N) дійсного
типу, які обчислюють значення факторіала N! і подвійного фак-
торіала N !!, відповідно (N> 0 – параметр цілого типу). За допо-
могою цих функцій обчислити факторіали та подвійні факторіа-
ли п'яти даних чисел.
3. Написати рекурсивну функцію PowerN (x, n) дійсного типу,
яка знаходить значення n-го степеня числа x за формулою:
x0 = 1, xn = x·xn–1 при n > 0 xn = 1 / x–n/xn при n < 0 (x > = 0 –
дійсне число, n – ціле). За допомогою цієї функції знайти зна-
чення xn при 5 різних значеннях n.
4. Написати рекурсивну функцію SqrtK (x, k, n) дійсного ти-
пу, яка знаходить наближене значення кореня k-го степеня з
числа x за формулою: y(n+1) = y(n) – (y(n) – x/y(n)k–1)/k, де y(n)
позначає SqrtK (x, k, n) (x – речовинний параметр, k та n – цілі;
x > 0, k > 1, n > 0). За допомогою цієї функції знайти наближені
значення кореня k-го степеня з x за шести різних значень n для
даних x та k.
5. Написати рекурсивну функцію FibRec (N) цілого типу, яка
обчислює N-те число Фібоначчі F (N) за формулою: F (1) = F (2)
= 1, F (k) = F (k-2) + F (k -1), k = 3, 4, .... За допомогою цієї функ-
ції знайти п'ять чисел Фібоначчі із зазначеними номерами й ви-
вести ці числа разом з кількістю рекурсивних викликів функції
FibRec, які знадобились для їх знаходження.
6. Написати рекурсивну функцію C (m, n) цілого типу, яка
знаходить кількість поєднань із n елементів по m, використову-
ючи формулу: C (0, n) = C (n, n) = 1, C (m, n) = C (m, n-1) + C (m-
1, n-1) при 0 < m < n (m та n – цілі параметри; n > 0, 0 <= m <= n).
Дано число N і п'ять різних значень M. Вивести числа C (M, N)
разом з кількістю рекурсивних викликів функції C, які знадоби-
лись для їх знаходження.
Написати рекурсивну функцію NOD (A, B) цілого типу, яка
знаходить найбільший спільний дільник двох натуральних чисел
A та B, використовуючи алгоритм Евкліда: NOD (A, B) = NOD
253
(B mod A, A), якщо A <> 0; NOD (0, B) = B. За допомогою цієї
функції знайти найбільші спільні дільники пар A та B, A та C, A
та D, якщо дано числа A, B, C, D.
7. Написати рекурсивну функцію
MinRec(A,N)1|MaxRec(A,N)2
дійсного типу, яка знаходить мінімальний1/максимальний2 еле-
мент речового масиву A розміром N, не використовуючи опера-
тор циклу. За допомогою функції MinRec1|MaxRec2 знайти міні-
мальні1|максимальні2 елементи масивів A, B, C розміром NA,
NB, NC, відповідно.
8. Написати рекурсивну функцію Digits (S) цілого типу, яка
знаходить кількість цифр у рядку S без використання оператора
циклу. За допомогою цієї функції знайти кількість цифр у даних
п'яти рядках.
9. Написати рекурсивну функцію Simm (S) логічного типу, яка
перевіряє, чи є симетричним рядок S, без використання оператора
циклу. За допомогою цієї функції перевірити дані п'ять рядків.
2.9.1. Масиви
255
Нижче показано, як призначаються елементи масиву для 30
чисел, що зображують червневі температури.
Псевдокод
var
<ім'я масиву>: array[n1. . n2] of<тип елементів масиву>.
Притримуючись зазначеної схеми, заданий масив опишемо так:
var
t: array[0..29] ofinteger;
С++
<тип елементів масиву><ім'я масиву>[кількість елементів масиву];
Притримуючись зазначеної схеми, заданий масив опишемо так:
int arr[30];
Python
Оголошення відбувається при ініціалізації, щоб ініціалізувати масив
із 30 елементів і заповнити його нулями, потрібно написати:
<ім'я масиву> = [0]*30
256
В описі <ім'я масиву> – це ідентифікатор, який задовольняє
всі потреби мови Python. Також необхідно вказати тип елементів
масиву (не тип індексів, а саме тип значення елементів).
Масив описано. Тепер опишемо інші змінні, що задіяні в на-
шій програмі.
Для організації циклів нам потрібен лічильник. Позначимо
його традиційно i – тип цілий; потрібен також лічильник кілько-
сті днів з температурою вище 23о, для нього створимо змінну k –
тип цілий; для підрахунку середнього значення температури
потрібно знайти її суму, для чого вкажемо змінну s, а оскільки в
ній буде зберігатись і середнє значення, то тип має бути дійсний
(ділення суми чисел на 30 може зумовити дробовий результат).
Отже, увесь опис буде таким:
Псевдокод
var
t : array[0. . 29] of integer;
i, k : integer;
s : real;
С++
int arr[30];
int i, k;
double s;
Python
arr = [0]*30
усі інші змінні до ініціалізації не оголошені
257
Для нашого прикладу це може бути записано так:
Псевдокод
type
a= array[0..29] of integer;
var
t: a;
С++
type def int a[30];
a arr2;
Python
Python не має такого способу.
Псевдокод
for i:=0 to 29 do
begin
write('Введіть температуру в ',i+1, '- день ');
readln(t[i])
end;
С++
for (i = 0; i < 30; i++)
{
printf("Введіть температуру в %d день ", i + 1);
scanf("%d", &arr[i]);
}
Python
for i in range(30) :
print("Введіть температуру в", i+1, "день")
arr[i]=int(input())
258
Наступний етап – це додавання температури й певної кілько-
сті днів з температурою вище 23о.
Зрозуміло, що перед початком циклу потрібно обнулити лі-
чильники s і k. Цей фрагмент програми буде таким:
Псевдокод
s:=0; k:=0;
for i:=0 to 29 do
begin
s:=s+t[i];
if t[i] > 23 then k:=k+1
end;
С++
s = 0; k = 0;
for (i = 0; i < 30; i++)
{
s += arr[i];
if (arr[i] > 23)
k++;
}
Python
s, k = 0, 0
for i in range(30) :
s += arr[i]
if arr[i]>23:
k+=1
Програма
Псевдокод
Program Temperature;
var
t: array[0..29] of integer;
i, k: integer;
s: real;
begin
for i:=0to 29do
begin
write('Введіть температуру в ',i,'- день ');
readln(t[i])
end;
s:=0; k:=0;
for i:=0 to 29 do
begin
s:=s+t[i];
if t[i] > 23 then k:=k+1
end;
writeln('Середня температура в червні ', s/30:4:2);
writeln('Кількість днів з температурою більше 23о ', k)
end.
С++
int main()
{
int arr[30];
int i, k;
double s;
for (i = 0; i < 30; i++)
{
printf("Введіть температуру в %d день ", i + 1);
scanf("%d", &arr[i]);
}
s = 0; k = 0;
for (i = 0; i < 30; i++)
{
s += arr[i];
260
if (arr[i] > 23)
k++;
}
printf("Cередня температура в червні %4.2lf\n", s / 30);
printf("Кількість днів з температурою вище 23о %d\n", k);
return 0;
}
Python
arr = [0]*30
for i in range(30) :
print("Введіть температуру в", i+1, "день")
arr[i]=int(input())
s, k = 0, 0
for i in range(30) :
s += arr[i]
if arr[i]>23:
k+=1
print("Cередня температура в червні","%4.2f"% (s / 30));
print("Кількість днів з температурою вище 23о", k);
262
Такій опис може використовуватись для створення кількох
констант.
Const int n = 100; m = 25; k = 1000;
Опис констант виконується перед описом типу, тобто споча-
тку описуються константи, потім – типи, потім – змінні тощо.
Далі в програмах ми будемо використовувати константи.
Тепер повернемось до нашої задачі. У ній не вказані конкре-
тні 20 чисел, тому їх можна взяти випадково, що дає можливість
заповнити масив за допомогою функції випадкових чисел.
Отже, описуємо масив із 20 чисел і змінні, які будуть викори-
стовуватись у програмі:
Псевдокод
Const
n = 20;
type
t = array[0. . (n-1)] of integer;
var
a: t;
s, i: integer;
С++
Const int n = 20;
typedef int t[n];
t a;
int s, i;
Python
У Python немає змінних-констант, але можна зробити так:
n=20;
a=[0]*n
Або навіть так, що дозволяє задавати розмір масиву безпосередньо
при виконанні програми:
n=int(input())
a=[0]*n
263
Розглянемо особливості побудови процедур.
Може скластися враження, що оголошення змінних у списку
формальних параметрів процедури нічим не відрізняється від
оголошення їх у розділі опису змінних. Насправді існує багато
спільного між описом змінних в основній програмі та формаль-
ними змінними в процедурі, але є одна дуже важлива різниця:
типом будь-якого параметра в списку формальних параметрів
процедури може бути тільки стандартний (int, float тощо) або
раніше оголошений користувацький тип. Оскільки в списку фо-
рмальних параметрів фактично оголошується тип-діапазон, який
указує границі індексів масиву, то не можна оголосити таке:
Псевдокод
Procedure create(a: array[0. . 19] of integer);
С++
void create(typedef int a[30])
Наприклад:
Псевдокод
const
n = 20;
type
t = array[0…(n-1)] of integer;
Procedure create(a: t);
С++
Const int n = 20;
Typedef int t[n];
void create(t a);
Python
У Python оголошувати нічого не потрібно
def create(a)
264
writeln('Задано масив цілих чисел');
for i:=0 to n-1 do
begin
a[i]:=random(201)- 100;
write(a[i], ' ')
end;
writeln
end;
С++
#include<ctime>
void create(int n, ta)
{
srand(time(0));
printf("Задано масив чисел\n");
for (int i = 0; i <n; i++)
{
a[i] = rand() % 201 – 100;
printf("%d ", a[i]);
}
}
Python
Def create(n,a):
print("Задано масив цілих чисел")
for i in range(n):
a[i]=randint(-100,101)
print(a)
Псевдокод
s:=0;
for i:=0 to n-1 do
if i mod 2 = 0 then s:=s+a[i];
writeln ('Сума елементів з парними номерами дорівнює ', s)
int s = 0;
265
С++
for (int i = 0; i < 20; i++)
if (i % 2 == 0) s += a[i];
printf ("Сума елементів з парними номерами дорівнює
%d\n", s);
Python
s=0
for i in range(n):
if i % 2 == 0 :
s += a[i]
print ("Сума елементів з парними номерами дорівнює", s);
Програма
Псевдокод
Program Problem2;
const
n = 20;
type
t = array[0..(n-1)] of integer;
var
a : t;
s, i: integer;
Procedure create(n: integer; var a: t);
var
i: integer;
begin
randomize;
writeln ('Задано масив цілих чисел');
for i:=0to n-1do
begin
a[i]:=random(201)-100;
write(a[i], ' ')
end;
writeln
end;
begin
create(n, a);
s:=0;
for i:=0 to n-1 do
if i mod 2 = 0 then s:=s+a[i];
writeln (Сума елементів з парними номерами дорівнює ', s)
end.
266
С++
#include<ctime>
Const int n = 20;
Typedef int t[n];
void create(int n, ta)
{
srand(time(0));
printf ("Задано масив цілих чисел\n");
for (int i = 0; i < n; i++)
{
a[i] = rand() % 201 – 100;
printf("%d ", a[i]);
}
printf("\n");
}
int main()
{
t a;
create(n, a);
int s = 0;
for (int i = 0; i < 20; i++)
if (i % 2 == 0)
s = s + a[i];
printf ("Сума елементів з парними номерами дорівнює
%d\n", s);
return 0;
}
Python
from random import randint
def create(n,a):
print ("Задано масив цілих чисел ")
for i in range(n):
a[i]=randint(-100,101)
print(a)
n=20
a=[0]*n
create(n,a)
s=0
for i in range(n):
if i % 2 == 0 :
s = s + a[i]
print ("Сума елементів з парними номерами дорівнює ", s)
267
Два цикли (заповнення масиву і додавання) можна об'єднати
в один, тоді програма буде значно кращою:
Псевдокод
Program Problem2;
const
n = 20;
type
t = array[0..(n-1)] of integer;
var
a: t;
s, i: integer;
begin
randomize;
writeln ('Задано масив цілих чисел');
s:=0;
for i:=0 to n-1 do
begin
a[i]:=random(201)-100;
write(a[i], ' ');
if i mod 2 = 0 then s:=s+a[i]
end;
writeln;
writeln ('Сума елементів з парними номерами дорівнює ', s)
end.
С++
#include<ctime>
const int n = 20;
typedef int t[n];
int main()
{
t a;
srand(time(0));
int s = 0;
printf ("Задано масив цілих чисел \n");
for (int i = 0; i < 20; i++)
{
a[i] = rand() % 201 – 100;
printf("%d ", a[i]);
if (i % 2 == 0)
s = s + a[i];
}
printf("\n Сума елементів з парними номерами дорівнює
268
%d\n", s);
return 0;
}
Python
From random import randint
n=20;
a=[0]*n
s=0
print ("Задано масив цілих чисел")
for i in range(n):
a[i]=randint(-100,101)
if i % 2 == 0 :
s = s + a[i]
print(a)
print ("Сума елементів з парними номерами дорівнює", s)
Псевдокод
type
mas=array[0..9]of Real;
і тоді в процедурі можна вказувати такий опис:
Procedure S (a: mas);
С++
Typedef double mas[10];
і тоді в процедурі можна вказувати такий опис:
void S(mas)
Python
def S(a)
269
Відкритий масив є формальним параметром підпрограми,
який описує базовий тип елементів масиву, але не визначає його
розмірність і границі:
Псевдокод
Procedure Test(a: array of Integer);
С++
void S(int a[], int L)
Python
Def S(a)
Псевдокод
Procedure ArrayPrint(a: array of Integer);
var
k: Integer;
begin
for k:=0 to High(a) do
Write(a[k]);
WriteLn
end;
С++
void ArrayPrint(int a[], int len)
{
srand(time(0));
printf("Заданий масив чисел\n");
for (int k = 0; k <len; k++)
{
printf("%d ", a[k]);
}
printf("\n");
}
270
Python
def ArrayPrint(a):
for i in range(len(a)):
print(a[i]);
271
Python
def transp_two(k, m, a):
p = a[k]
a[k] = a[m]
a[m] = p
for i inrange(len(a)):
print(a[i])
272
begin
create(n, a);
write('Введіть номера елементів, що перестав.'); readln(k, m);
writeln('Масив після перестановки ', k, '- го і ', m, '-го елементів');
transp_two(k-1, m-1, a);
end.
C++
#include<ctime>
Const int n = 10;
Typedef int t[n];
void create(t a)
{
srand(time(0));
printf("Заданий масив цілих чисел \n");
for (int i = 0; i < n; i++)
{
a[i] = rand() % 201 – 100;
printf("%d ", a[i]);
}
printf("\n");
}
void transp_two(intk, intm, inta[])
{
int p = a[k]; a[k] = a[m]; a[m] = p;
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
int main()
{
t a;
int k, m;
create(a);
printf("Введіть номера елементів, що перестав.\n");
printf("Введіть k\n");
scanf("%d", &k);
printf("Введіть m\n");
scanf("%d", &m);
printf("Масив після перестановки %d-го і %d-го елемен-
тів\n", k, m);
transp_two(k – 1, m – 1, a);
return 0;
}
273
Python
from random import randint
def create(a):
print("Заданий масив цілих чисел")
for i in range(len(a)):
a[i]=randint(100,201)
print(a)
def transp_two(k, m, a):
p = a[k];a[k] = a[m]; a[m] = p
print(a)
n=10;
a=[0]*n
create(a)
print("Введіть номера елементів, що перестав.")
print("Введіть k")
k = int(input())
print("Введіть m")
m = int(input())
print("Масив після перестановки ", k, "-го і", m,"-го елементів")
transp_two(k – 1, m – 1, a);
275
Procedure create(n: integer; var a: t);
var
i: integer;
begin
randomize;
writeln('Заданий масив цілих чисел');
for i:=0 to (n-1) do
begin
a[i]:=random(201)- 100; write(a[i], ' ')
end;
writeln
end;
Procedure search_number(d: integer; a: t; var k: integer);
var
i: integer;
begin
i:=0;
repeat
i:=i+1
until a[i] = d;
k:=i
end;
Procedure transp2begin(k: integer; var a: t);
var
i, p: integer;
begin
for i:=k downto 1 do
begin
p:=a[i-1]; [i-1]:=a[i]; a[i]:=p
end
end;
begin
create(n, a);
write('Введіть елемент, що переставляється '); readln(d);
search_number(d, a, k);
transp2begin(k, a);
writeln('Масив після перестановки елементів у початок');
for i:=1 to n do write(a[i], ' ');
writeln
end.
276
С++
#include<ctime>
const int n = 20;
typedef int t[n];
void create(ta)
{
srand(time(0));
printf("Заданий масив цілих чисел \n");
for (int i = 0; i < n; i++)
{
a[i] = rand() % 201 – 100;
printf("%d ", a[i]);
}
printf("\n");
}
void search_number(intd, ta, int* k)
{
*k = 0;
while (a[*k] != d) {
*k+= 1;
}
}
void transp2begin(intk, ta)
{
int p;
for (int i = k; i > 0; i–)
{
p = a[i – 1];
a[i – 1] = a[i];
a[i] = p;
}
}
int main()
{
t a;
int d, k;
create(a);
printf("Введіть елемент, що переставляється \n");
scanf("%d", &d);
search_number(d, a, &k);
transp2begin(k, a);
printf("Масив після перестановки елементів у початок \n");
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
277
printf("\n");
return 0;
}
Python
From random import randint
def search_number(d, a, k):
k[0] = 0;
while a[k[0]] != d:
k[0] += 1;
def create(a):
print("Заданий масив цілих чисел")
for i in range(len(a)):
a[i] = randint(-100,101)
print(a)
def transp2begin(k, a):
for i in range(k,0,-1):
p = a[i – 1]
a[i – 1] = a[i]
a[i] = p
n=20
a=[0]*n
create(a)
print("Введіть елемент, що переставляється");
d=int(input())
k=[0]
search_number(d, a, k);
transp2begin(k[0], a);
print("Масив після перестановки елементів у початок")
print(a)
278
2.9.4. Методи сортування елементів масиву
280
Вправи
1. Створити програму видалення k-го елемента з масиву a(n);
включення елемента d у k-ту позицію масиву a(n).
2. Змінити порядок елементів одновимірного масиву на обер-
нений.
3. В одновимірному масиві знайти кількість його від'ємних
елементів.
4. Дано масив c(n). Знайдіть кількість його додатних елемен-
тів і виведіть на екран їхні індекси та самі елементи.
5. Написати програму обрахунку суми тих елементів цілочи-
слового масиву n0, n1, ..., n100, які є парними числами.
6. Дано масив a1, a2, ..., a200. Написати програму створення
масивів b1, b2, ..., b100 та c1, c2, ..., c100, елементи яких дорівнюють
відповідно значенням a1, a3, ..., a199 та a2, a4, ..., a200.
7. Написати програму визначення суми непарних від'ємних
елементів у заданому цілочисловому масиві a1, a2, ..., an.
8. Дано масиви a1, a2, ..., an та b1, b2, ..., bm. Написати програму
побудови масиву c1, .... cm+n, елементи якого відповідно дорів-
нюють a1, ..., an, b1, ..., bm.
9. Написати програму виконання таких задач: у масиві чисел
a1, ..., an:
а) замінити число b на число c;
б) підрахувати кількість елементів ai, для яких виконано
q ≤ ai ≤ p, де p та q – задані числа;
в) знайти суму додатних і кількість від'ємних елементів;
г) знайти суму й добуток усіх елементів.
Для розв'язання всіх задач досить одного перегляду масиву
a1, ..., an (одного циклу).
10. Перемістіть елемент масиву a(n) праворуч (ліворуч) на m
позицій, при цьому m-й елемент із кінця масиву перемістіть у
початок (або навпаки).
11. Написати програму, після виконання якої стане відомо, чи
є в цілочисловому масиві c-1, c0, c1,..., c1000 два підряд нульові
елементи.
12. Створити програму побудови вектора a1, a2, ..., a200, елеме-
нти якого відповідно дорівнюють значенням b1, c1, b2, c2,..., b100,
c100, де b1, b2, ..., b100 та c1, c2, ..., c100 – вектори, що дані.
281
13. Написати програми, при виконанні яких у масиві a1, a2, ...,
an визначається кількість сусідніх:
а) двох додатних чисел;
б) двох чисел різного знака;
в) двох чисел одного знака, причому абсолютна величина
першого числа має бути більше другого числа.
14. Дано масив a1, a2, ..., a2n. Написати програму побудови ма-
сивів з елементами, що дорівнюють:
а) a1, an+1, a2, an+2, ..., an, a2n;
б) a2n, a1, a2n-1, a2, ..., an+1, an.
15. Написати програму знаходження найбільшого спільного
дільника елементів цілочислового масиву a1, a2, ..., an.
У програмі описати й використати процедуру знаходження
найбільшого спільного дільника двох цілих чисел.
Сортування простими вставками. Для викладення ідеї ме-
тоду сортування простими вставками знадобляться знання про
пошук і вставлення елемента в упорядкованому масиві. Спочат-
ку розв'яжемо таку задачу.
Приклад 2.22. Задання пошуку елемента в упорядкованому
масиві.
Нехай задано цілочисловий масив a1 < ... < an і ціле число b.
З'ясувати, чи входить число b у заданий масив, а якщо входить,
то яке значення номера елемента, що дорівнює цьому числу,
тобто знайти таке p, для якого ap = b.
Якщо масив невпорядкований, то єдиний спосіб пошуку – по
черзі порівнювати задане число b з елементами масиву, почи-
наючи з першого, і продовжувати доти, поки не натрапимо на
рівний числу b елемент.
Якщо виявиться, що після перегляду і порівняння всіх елеме-
нтів масиву елемента, що дорівнює числу b, не знайдеться, то
видати повідомлення, що такого елемента в масиві немає.
Однак для впорядкованих даних є метод, який дає можли-
вість значно швидше відшукати рівний числу b елемент (або
з'ясувати, що такого немає). За С. А. Абрамовим називатимемо
цей метод "розділяй і володарюй".
282
Ідея методу дуже проста. Вважатимемо, що масив упорядко-
ваний за зростанням, тобто ( a1 ≤ a2 ≤ ... ≤ an ).
Якщо в масиві непарна кількість елементів, то назвемо серед-
нім елемент, який міститься посередині масиву. Наприклад, якщо
кількість елементів 17, то середнім буде елемент під номером 9.
Якщо ж кількість елементів парна, наприклад 20, то середнім
вважатимемо елемент під номером, який отримаємо від цілочи-
слового ділення суми 1 + 20 на 2, тобто (1 + 20) div 2 = 10. Для
масиву з кількістю елементів 124 середнім буде елемент під
номером 62 (125 ділимо націло на 2) тощо.
Ідея методу.
1. Вибираємо середній елемент масиву і порівнюємо із зада-
ним числом b.
Випадок 1. Обраний середній елемент менше числа b, тоді
немає сенсу розглядати ліву частину масиву (там рівного елеме-
нта точно не буде), тому шукаємо в правій частині масиву.
Випадок 2. Обраний середній елемент більше або дорівнює
заданому числу b, тоді немає сенсу розглядати праву частину
масиву, пошук триває серед елементів лівої частини.
Таким чином, кількість випробувань скорочується вдвічі.
2. З тією половиною масиву, у якій установлено, що може
бути рівний елемент, виконується та сама процедура розподілу.
3. Процес розподілу триває. Нарешті настає момент, коли в
обраній частині масиву залишається один елемент (тобто номе-
ра елементів ліворуч і праворуч стануть рівними), тоді він є шу-
каним; якщо ж він не дорівнює заданому числу, то такого еле-
мента взагалі немає в масиві. Для кращого розуміння процесу
розглянемо простий приклад.
Приклад 2.23. Нехай задано масив з 10 упорядкованих за
зростанням чисел:
23 34 45 48 56 63 67 72 78 89
Треба знайти елемент масиву, рівний числу 78.
Нехай p – номер елемента лівої межі масиву, q – номер еле-
мента правої межі масиву, s – номер середнього елемента.
Знаходимо середній елемент. Їм буде елемент під номером
s = ( p + q) \ 2 = (1 + 10) \ 2 = 5 (знак "\" – знак цілочислового ді-
лення), тобто 56.
283
Порівняємо його з числом 78: 56 <78. Отже, шуканий еле-
мент міститься праворуч від 56.
Розглядаємо праву половину масиву, тобто елементи, що мі-
стяться праворуч від 56:
63 67 72 78 89
Тоді p = 6, q = 10.
Вибираємо середній елемент. Їм буде елемент під номером
s = (p + q) \ 2 = (6 + 10) = 8, рівний 72.
Порівнюємо: 72 <78; отже, знову шуканий елемент міститься
праворуч від 72, тобто в такій частині масиву:
78 89
9 10
p = 9, q = 10.
Знову знаходимо середній елемент. Їм буде елемент під но-
мером
s = (p + q) \ 2 = (9 + 10) \ 2 = 9.
Порівнюємо: 78 <78.
Умова не виконується. Значить, треба брати елементи масиву
ліворуч від числа 78.
Тоді p = 9, q = 9. Номери лівої та правої меж стали рівними,
цикл закінчується, шуканим елементом буде елемент під номе-
ром 9, що дорівнює 78.
Справді: 78 = 78.
Зверніть увагу, що при русі праворуч, тобто коли розглядає-
мо праву частину масиву, граничний елемент (він же колишній
середній, з яким порівнюємо) у цю частину масиву не включа-
ється, а при русі ліворуч середній елемент включається в решту.
Алгоритм
Спочатку за ліву і праву межі беремо значення 1 та n. Нехай
p та q позначають ліву і праву межі, тоді p: = 1, а q: = n; далі ви-
конується розподіл і крок за кроком межі зближуються. Процес
поділу має тривати до збігу меж, коли p = q, тобто цикл викону-
ється, поки ( p < q). Межі зближуються таким чином: нехай s –
номер середнього елемента, значення s – це ціла частина серед-
нього арифметичного меж p та q ( s := ( p + q ) div 2).
284
Якщо as < b, то треба змінити колишню нижню межу на
s + 1 ( p := s + 1), а верхня межа (q) залишається без змін (виби-
рається права частина масиву); інакше залишити без змін ниж-
ню межу (p), а верхню замінити на s (q := s).
Тепер можна записати алгоритм у вигляді процедури.
{Процедура швидкого пошуку номера елемента в упорядко-
ваному масиві}
Псевдокод
Procedure quick_search(int[] a; int n, int b,returnint p);
Int q,s;
begin
p:=1; q:=n;
while p < q do
begin
s:=(p+q) div 2;
if a[s] > b then p:=s+1
else q:=s
end
end;
Python C++
def quick_search(a, n, b, p): void quick_search (int[] a, int n, int b,
p = 1; int*p)
q = n; {
while p < q : int q, s;
s = (p + q) // 2; p = 1;
if a[s] > b : q = n;
p = s + 1; while (p < q)
else: {
q = s; s = (p + q) % 2;
return p if (a[s] > b)
p = s + 1;
else
q = s;
}
}
Псевдокод
Procedure insert(int n,int[] a);
Int p, q, s, k, c;
begin
for i:=2 to n do
begin
p:=1; q:=i;
c:=a[i];
while p < q do
begin
s:=(p+q) div 2;
if c > a[s] then
p:=s+1 else q:=s
end;
287
for k:=i downto p+1 do a[k]:=a[k- 1];
a[p]:=c
end
end;
Python C++
def insert(n, a): voidinsert (int n, int[] *a)
for i in range(2, n) : {
p = 1; int p, q, s, k, c;
q = n; for (int i = 2; i < 2; i++)
c = a[i]; {
while p < q : p = 1; q = n;
s = (p + q) % 2; c = a[i];
if c > a[s] : while (p < q)
p = s + 1; {
else: s = (p + q) % 2;
q = s; if (c > a[s])
for k in range(i, p+1, -1) : p = s + 1;
a[k] = a[k – 1]; else
a[p] = c; q = s;
return a }
for (int k = i; k < p+1; i–)
{
a[k] = a[k-1];
a[p] = c;
}
}
}
288
Нехай задано масив чисел (знову для простоти міркувань ві-
зьмемо масив цілих чисел):
123 2510 6 8 3465 44 17
Позначимо його ім'ям a. У нашому прикладі він має 10 елементів.
Уведемо ще один масив із 10 тих самих елементів, але впоря-
дкованих за зростанням. Наше завдання полягатиме в побудові
цього масиву.
За перший елемент упорядкованого масиву b візьмемо пер-
ший елемент масиву a.
Отже, масив b:
12
Тепер беремо другий елемент масиву a – це 3 – і за допомо-
гою процедури швидкого пошуку вставляємо його в упорядко-
ваний масив b. Оскільки 3 менше 12, то він вставиться перед 12,
і впорядкований масив b уже буде складатися із двох елементів:
3 12
Беремо третій елемент масиву a – 25 – і вставляємо в масив b,
одержуємо:
3 12 25
Продовжуватимемо такі дії до тих пір, поки всі n елементів
масиву a не будуть переглянуті. У результаті утворюється новий
упорядкований масив b.
Як усе просто вийшло! Беремо елемент масиву a і вставляємо
в упорядкований масив b, використовуючи процедуру швидкого
пошуку місця елемента в упорядкованому масиві b.
Схема програми:
Program Problem4; {Сортування простими вставками}
опису змінних і масивів;
процедура створення масиву;
процедура швидкого пошуку номера елемента в
упорядкованому масиві;
процедура переміщення елементів у кінець масиву;
begin
виклик процедури створення масиву;
задається перший елемент нового впорядкованого масиву:
b [1]: = a [1];
цикл за кількістю елементів, що залишилися в масиві a,
починаючи від 2:
for i: = 2 to n do
289
begin
звернення до процедури швидкого пошуку номера
елемента в масиві b для елемента масиву a [i];
виклик процедури переміщення елементів у кінець
масиву b;
вставка i-го елемента масиву a на знайдене місце:
b [p]: = a [i]
end;
виведення впорядкованого масиву b на екран
end.
Програма:
Псевдокод
ProgramProblem4; {Сортування простими вставками}
const n := 20;
Int[] t := array[1..n] of Int;
Int[] a := t;
Int[] b := t;
Int: i, p;
Procedure create(Int n; returnInt[] a);
Int i;
begin
writeln('Заданий масив цілих чисел');
for i:=1 to n do
begin
a[i]:=random(201)-100;
write(a[i], ' ')
end;
writeln
end;
Procedure quick_search(Int m, Int c,Int[] b,return Int p);
Int q,s;
begin
p:=1; q:=n;
while p < q do
begin
s:=(p+q) div 2;
if a[s] > b then p:=s+1
else q:=s
end
end;
290
Procedure movement_end(Int i, Int k; return Int[] b);
Int j;
begin
for j:=i downto k+1 do
b[j]:=b[j- 1]
end;
begin
create(n, a);
b[1]:=a[1];
for i:=2 to n do
begin
quick_search(i, a[i], b, p);
movement_end(i, p, b);
b[p]:=a[i]
end;
writeln;
writeln('Масив упорядкований за зростанням');
for i:=1 to n do
write(b[i], ' ');
writeln;
end.
Python C++
n = 20; void quick_search (int[] a, int n, int
t = [0]*n; b, int*p)
a = t; {
b = t; int q, s;
i, p; p = 1;
q = n;
def quick_search(a, n, b, p): while (p < q)
p = 1; {
q = n; s = (p + q) % 2;
while p < q : if (a[s] > b)
s = (p + q) // 2; p = s + 1;
if a[s] > b : else
p = s + 1; q = s;
else: }
q = s; }
return p
void create(int n, int[] *a)
def create(n, a); {
print('Заданий масив цілих cout<< “Заданий масив цілих чи-
чисел'); сел”;
291
for i in range(1, n): for(int i=1; i<n; i++)
a[i]=random.randint(-100, {
100); a[i]=(rand()%201)-100;
print(a[i]); cout<< a[i] << “ “;
print(''); }
return a; cout<<endl;
}
def movement_end(i, k,b);
j = i; void movement_end(int i, int k,int[]
for j in range(j, k+1, -1): *b)
b[j]=b[j-1]; {
create(n, a); for(int j=i; i<k+1; i–)
b[1]=a[1]; b[j]=b[j-1];
for i in range(2, n): create(n, a);
quick_search(i, a[i], b, p); b[1]=a[1];
movement_end(i, p, b); for(i=2; i<n; i++)
{
b[p]=a[i]; quick_search(i, a[i], b, p);
print(''); movement_end(i, p, b);
print('Масив упорядкова- b[p]=a[i];
ний за зростанням'); }
for i in range(1, n): cout<<endl;
print(b[i]); cout<< “Масив упорядкований за
print(''); зростанням”
return b; <<endl;
for(i=1; i<n; i++)
cout<<b[i]<< “”;
cout<<endl;
}
int main ()
{
constintn = 20;
int[] t = new int[n];
int[] a = t;
int[] b = t;
inti, p;
movement_end(i, p ,b);
}
292
Уважно вивчіть програму і спробуйте відповісти на такі запитання:
1) Чому в процедурі швидкого пошуку права межа береться
до m, а не до m + 1 (p: = 1; q: = m;)?
2) Чому при звільненні місця під елемент, що вставляється в
масив b, цикл організовується від i до p + 1 (for k: = i downto p +
1 do b [k]: = b [k-1];)?
3) Чи можна не вводити в процедуру швидкого пошуку но-
мер елемента i (search (i, a [i], b, p)?
Зрозуміло, що недоліком програми є присутність у ній друго-
го масиву b. Чи можна обійтися без нього?
Спробуємо скласти процедуру сортування простими встав-
ками, не використовуючи додатковий масив. Для цього досить у
процедуру search перенести цикл для перегляду елементів маси-
ву a від 2 до n, і такий цикл має бути зовнішнім щодо всіх інших
операторів, які виконуються всередині процедури:
for i: = 2 to n do
begin
На початку циклу встановимо номери елементів нового впо-
рядкованого масиву, а спочатку він складається із одного еле-
мента – першого; значить, ліва межа p: = 1, а права q: = i.
Перший елемент, який візьмемо для вставки в упорядкований
масив, – це елемент під номером 2, тобто a [i]. Його значення
дамо змінній c: = a [i].
Далі починається вже відома вам процедура швидкого пошу-
ку елемента в упорядкованому масиві (який поки складається із
одного елемента):
while p <q do
begin
s: = (p + q) div 2;
if c> a [s] then p: = s + 1 else q: = s
end;
Коли знайдено для цього елемента місце, його треба усунути,
тому всі елементи нового масиву треба зсунути на 1 ліворуч, що
виконується за допомогою циклу:
for k: = i downto p + 1 do a [k]: = a [k- 1];
Нарешті, останній крок – вставити елемент c на звільнене місце:
a [p]: = c.
293
Оформимо програму з процедурою вставки:
Псевдокод
ProgramProblem4; {Сортування простими вставками}
const n := 20;
Int[] t := array[1..n] of Int;
Int[] a := t;
Int: i, p;
Procedure create(Int n; returnInt[] a);
Int i;
begin
writeln('Заданий масив цілих чисел');
for i:=1 to n do
begin
a[i]:=random(201)-100;
write(a[i], ' ')
end;
writeln
end;
Procedure insert(int n,int[] a);
Int p, q, s, k, c;
begin
for i:=2 to n do
begin
p:=1; q:=i;
c:=a[i];
while p < q do
begin
s:=(p+q) div 2;
if c > a[s] then
p:=s+1 else q:=s
end;
for k:=i downto p+1 do a[k]:=a[k- 1];
a[p]:=c
end
end;
begin
create(n, a);
insert(n, a);
for i:=2 to n do
writeln;
writeln('Масив упорядкований за зростанням');
for i:=1 to n do
294
write(a[i], ' ');
writeln;
end.
Python C++
n = 20; voidinsert (int n, int[] *a)
t = [0]*n; {
a = t; int p, q, s, k, c;
i; for (int i = 2; i < 2; i++)
{
def create(n, a); p = 1; q = n;
print('Заданий масив цілих c = a[i];
чисел'); while (p < q)
fori in range(1, n): {
a[i]=random.randint(-100, 100); s = (p + q) % 2;
print(a[i]); if (c > a[s])
print(''); p = s + 1;
return a; else
q = s;
def insert(n, a): }
for i in range(2, n) : for (int k = i; k < p+1; i–)
p = 1; {
q = n; a[k] = a[k-1];
c = a[i]; a[p] = c;
while p < q : }
s = (p + q) % 2; }
if c > a[s] : }
p = s + 1;
else void create(int n, int[] *a)
q = s; {
for k in range(i, p+1, -1) : cout<< “Заданий масив цілих чи-
a[k] = a[k – 1]; сел”;
a[p] = c; for(int i=1; i<n; i++)
return a {
a[i]=(rand()%201)-100;
create(n, a); cout<< a[i] << “ “;
insert(n, a); }
print(''); cout<<endl;
print('Масив упорядкований за }
зростанням');
fori in range(1, n): int main ()
print(a[i]); {
295
print(''); const int n = 20;
int[] t = new int[n];
int[] a = t;
inti;
create(n, a);
insert(n, a);
cout<<endl;
cout<< “Масив упорядкований за
зростанням”
<<endl;
for(i=1; i<n; i++)
cout<<a[i]<< “”;
cout<<endl;
}
Вправи
1. В одновимірному масиві є елементи 0, 1, 2. Переставте їх
на перші три місця за зростанням.
2. Скласти програму визначення задуманого людиною числа
від 1 до 1000 за допомогою 10 запитань. Кожне запитання має
вигляд: "Чи правда, що задумане число більше k?" При цьому
вказується конкретне k. Відповіді людини – це 1 (Так) і 0 (Ні).
Застосувати ідею поділу навпіл ("розділяй і володарюй").
3. Дано два упорядковані масиви a (n) і b (m). Утворити з
їхніх елементів упорядкований масив c (n + m), тобто об'єднати
два упорядковані масиви в один упорядкований масив.
4. Написати програму стиснення масиву відкиданням нульових
елементів. Нулями заповнюється звільнений хвіст масиву.
5. Визначити кількість збіжних елементів двох упорядкованих
одновимірних масивів, створених за допомогою функції випадкових
чисел. Масиви треба створити за допомогою функції випадкових
чисел, упорядкувати за спаданням, а потім порівняти.
296
Спочатку найбільшим елементом вважатимемо перший, а по-
тім послідовно порівняємо його з іншими елементами масиву.
Як тільки зустрінеться елемент, більший за перший, його вважа-
тимемо найбільшим й продовжимо подальше порівняння, але
вже з новим найбільшим елементом, і так до кінця масиву.
При введенні масиву можна застосувати вже відому проце-
дуру create.
Для визначення найбільшого значення елементів масиву скла-
демо процедуру maximum, користуючись запропонованою блок-
схемою. Її скласти неважко. Початкове значення максимального
елемента нехай дорівнюватиме першому елементу: max:=a [1].
Далі, починаючи з другого, проглядаємо елементи масиву і
порівнюємо з максимальним. Якщо максимальний елемент рап-
том виявляється меншим за який-небудь новий елемент, то но-
вий елемент беремо за максимальний.
Псевдокод
Procedure maximum(n: integer; a: t; var max: integer);
var
i: integer;
begin
max:=a[1];
for i:=2 to n doif max < a[i] then max:=a[i]
end;
С++
void maximum(intn, inta[], int *max)
{
*max = a[0];
for (int i = 1; i <n; i++)
if (*max<a[i])
*max = a[i];
}
Python
def maximum(n, a, max):
max[0] = a[0];
for i inrange(1,n) :
if max[0] <a[i] :
max[0] = a[i]
297
Можуть бути інші варіанти процедур. Наведемо одну з них.
У ній задіяна рекурсія, тому вона досить оригінальна й допома-
гає нам учитися використовувати рекурсивні підпрограми.
Ідею складання рекурсивної процедури визначення найбіль-
шого елемента масиву продемонструємо на прикладі.
Нехай задано масив:
12 34 56 23 85 37 44 15 19 11
12 34 56 23 85 37 44 15 19 11
Процес починається з першого елемента – за найбільший бе-
реться саме він (у нашому випадку – це 12).
Потім береться наступний елемент 34 і порівнюється з найбі-
льшим 12, 34>12; отже, за найбільший береться 34. Далі порів-
нюється 56, 56 >34 тощо.
Однак процес потрібно "пригальмувати", інакше відбудеться
зациклення процедури. Для цього достатньо ввести в початок
процедури умовний оператор. Якщо n=0, то процедуру потрібно
закінчити, тобто виконати перехід у кінець процедури.
Рекурсивна процедура матиме такий вигляд:
Псевдокод
Procedure maximum(n: integer; a: t; var max: integer);
var
i: integer;
begin
max:=a[1];
for i:=2 to n do if max < a[i] then max:=a[i]
end;
С++
void maximum(intn, inta[], int *max)
{
*max = a[0];
for (int i = 1; i <n; i++)
if (*max<a[i])
*max = a[i];
}
Python
def maximum(n, a, max):
max[0] = a[0];
for i in range(1,n) :
if max[0] <a[i] :
max[0] = a[i]
298
Перевага тут не тільки в оригінальності процедури, яка ви-
кликає саму себе, а й у тому, що ми обходимося без традиційних
циклів, передбачених мовами програмування.
Приклад 2.27. Дано масиви a (n), b (k), c (m). Знайдіть зна-
чення найменшого елемента серед цих трьох масивів.
Можливі два способи розв'язання завдання.
Перший спосіб. Знайти найменші елементи кожного із трьох
масивів, а потім, використовуючи вже відомий алгоритм мен-
шого із трьох чисел, знайти найменший елемент.
Другий спосіб. Об'єднати всі три масиви в один d (n + k + m) і
знайти найменший елемент цього нового масиву.
Тепер можна перейти до написання процедури сортування
вибором.
Математичний аналіз завдання. Для сортування масивів мо-
жна використовувати таку просту ідею:
у заданому масиві визначити найбільший елемент і переста-
вити його на останнє місце, а останній елемент поставити на
місце найменшого;
після цього розглянути масив без останнього елемента і ви-
конати ті самі операції пошуку і перестановки тощо, поки не
дійдемо до першого елемента.
Процес можна проілюструвати на частковому прикладі.
Нехай задано масив із восьми елементів:
5, 3, 57, -12, 6, -16, 21, 25.
Визначаємо його найбільший елемент, що дорівнює 57, і пе-
реставляємо з першим елементом. Отримуємо масив:
5, 3, 25, -12, 6, -16, 21, 57.
Тепер розглядаємо масив елементів
5, 3, 25, -12, 6, -16, 21.
У ньому визначаємо найбільший елемент, їм є 25, і перестав-
ляємо із останнім елементом. Масив стане таким:
5, 3, 21,-12, 6,-16, 25.
Продовжуємо до останнього елемента, коли, нарешті, масив
стане впорядкованим за зростанням:
-16,-12, 3, 5, 6, 7, 21, 25.
Схема програми:
299
Програма працює за таким самим принципом, тільки шукає
найменший елемент (замість найбільшого) і переставляє в поча-
ток (замість перестановки в кінець).
Псевдокод
опис;
begin
ввести масив x;
for i:=1 to n do
begin
знайти індекс k найменшого елемента
xi, x(i+1), ..., xn;
переставити xi з xk;
end;
виведення відсортованого масиву;
end.
Деталізація
Пошук індексу k найменшого елемента серед x[i], ..., x[n];
k:=i;
forj:=i+1 tondo
ifx[j] <x[k] thenk:=j;
301
Програма
Псевдокод
Program Problem4;
const
n = 20;
type
t = array[1..n] ofinteger;
var
x, y: t;
p, i, j, k: integer;
begin
writeln('Задані значення аргументу');
randomize;
for i:=1 to n do
begin
x[i]:=random(101);
write(x[i], ' ');
y[i]:=random(101)
end;
writeln;
writeln('Задані значення функції');
for i:=1 to n do write(y[i], ' ');
writeln;
writeln('Упорядковані значення аргументу');
fori:=1 tondo
begin
k:=i;
for j:=i+1 to n doif x[j] < x[k] then k:=j;
p:=x[i]; x[i]:=x[k]; x[k]:=p; write(x[i],' ');
p:=y[i]; y[i]:=y[k]; y[k]:=p
end;
writeln;
writeln('Відповідні значення функцій');
for i:=1 to n do write(y[i], ' ');
writeln
end.
C++
int main()
{
constint n = 20;
int x[n], y[n];
int p, k;
302
printf("Задані значення аргументу\n");
srand(143);
for (int i = 0; i < n; i++)
{
x[i] = rand() % 101;
printf("%3d ",x[i]);
y[i] = rand() % 101;
}
printf("\n");
printf("Задані значення функцій\n");
for (int i = 0; i < n; i++) printf("%3d ", y[i]);
printf("\n");
printf("Упорядковані значення аргументу\n");
for (int i = 0; i < n; i++)
{
k = i;
for(int j = i + 1;j<n;j++)
if(x[j] < x[k])
k = j;
p = x[i]; x[i] = x[k]; x[k] = p; printf("%3d ", x[i]);
p = y[i]; y[i] = y[k]; y[k] = p;
}
printf("\n");
printf("Відповідні значення функції\n");
for (int i = 0; i < n; i++)
printf("%3d ", y[i]);
printf("\n");
return 0;
}
Python
n = 20
x=[0]*n
y=[0]*n
s1=""; s2=""
for i in range(n):
x[i] = randint(0,100); s1+="%3d "%x[i]
y[i] = randint(0,100); s2+="%3d "%y[i]
print("Задані значення аргументу")
print(s1)
print("Задані значення функції")
print(s2)
303
s1, s2 = "", ""
for i in range(n):
k = i;
for j in range(i + 1,n):
if x[j] < x[k] :
k=j
p = x[i]; x[i] = x[k]; x[k] = p; s1+="%3d "%x[i]
p = y[i]; y[i] = y[k]; y[k] = p; s2+="%3d "%y[i]
print("Упорядковані значення аргументу")
print(s1)
print("Відповідні значення функції")
print(s2)
Вправи
1. Скласти програму визначення найменшого елемента маси-
ву не менш ніж двома способами.
2. Дано масив зі 100 цілих чисел. Знайдіть різницю його най-
більшого та найменшого чисел.
3. Дано масиви a(n) та b(m). Чи є серед елементів масиву b(m)
елемент, що дорівнює найбільшому (найменшому) елементу
масиву a(n) (використовувати процедуру швидкого пошуку еле-
мента в упорядкованому масиві).
Написати програму пошуку найбільшого з від'ємних елемен-
тів даного масиву.
4. Знайти найменший з додатних елементів масиву
(x1, x2, ..., x40).
5. Знайти найбільше значення (xi + yi) для масивів (x1, x2, ...,
x40) та ( y1 , y 2 , ..., y 40 ).
6. Для масиву (a1, a2, ..., a80) обчислити найбільше та най-
менше значення модуля різниці між сусідніми елементами.
7. Дано цілі a1, ..., an. З модулів членів даної послідовності вибра-
ти найбільший. Отримати нову послідовність із n цілих чисел (нулів
та одиниць), замінюючи ai нулем, якщо модуль ai не збігається з
обраним значенням, і заміняючи ai одиницею, якщо збігається.
8. Дано дійсні a1, ..., an. Необхідно знайти b, що дорівнює се-
редньому арифметичному чисел a1, ..., an, і найбільше відхилен-
ня від середнього, тобто max(|a1- b|, |a2 - b| , ..., |an - b|).
304
Швидке сортування масиву. Сортувати дані доводиться до-
сить часто, тому виникає необхідність у швидших способах сор-
тування, ніж бульбашкове тощо. Навіть без точних математич-
них міркувань можна зрозуміти, що бульбашкове сортування
дуже тривале. За великої кількості даних, а на практиці так і є,
цим способом користуватися незручно.
Розберемо один з оригінальних способів сортування, який
увійшов в інформатику під назвою швидке сортування. Наведе-
мо основні міркування, що лежать у його основі.
Математичний аналіз завдання. Нехай задано невпорядко-
ваний масив цілих чисел (знову беремо цілі числа для простоти
запису):
45 33 12 18 67 26 15 36 48 89
Назвемо середнім той елемент масиву, ліворуч від якого бу-
дуть числа, менші за нього, а праворуч – більші.
Зрозуміло, що зовсім не обов'язково, щоб середній елемент
був посередині масиву, він іноді може міститися досить далеко
від середини.
Щоб знайти такий елемент, зробимо так. Візьмемо лівий
крайній елемент (45) і будемо порівнювати його з елементами
від правого кінця:
45 33 12 18 67 26 15 36 48 89
Якщо лівий елемент менше правого (45 <89), то все гаразд і
ми знову продовжуємо перегляд із правого кінця:
45 33 12 18 67 26 15 36 48 89
45 менше 48, тому знову йдемо далі до середини, але до се-
редини масиву переходити не будемо:
45 33 12 18 67 26 15 36 48 89
Натрапили на елемент 36, який менше 45. Переставляємо ці
елементи, 45 на місце 36, а 36 – на місце 45 і процес порівняння
з правого кінця припиняємо. Отримуємо такий масив:
36 33 12 18 67 26 15 45 48 89
Починаємо порівняння з лівого кінця масиву. Будемо порів-
нювати елементи ліворуч від 45, починаючи з першого, з елеме-
нтом 45:
36 33 12 18 67 26 15 45 48 89
36 менше 45, тому рухаємося далі праворуч:
305
36 33 12 18 67 26 15 45 48 89
33 менше 45, рухаємося праворуч:
36 33 12 18 67 26 15 45 48 89
12 менше 45, беремо наступний елемент:
36 33 12 18 67 26 15 45 48 89
18 менше 45, порівнюємо наступний елемент:
36 33 12 18 67 26 15 45 48 89
67 більше 45. Умова 67 <45 не виконується, отже, 67 містить-
ся не на своєму місці. Переставляємо його із 45, отримуємо:
36 33 12 18 45 26 15 67 48 89
Після такої перестановки перевіримо номера елементів, які
переставили останніми, дотримуючись вимоги, щоб номер ліво-
го елемента був менше номера правого; як тільки номери ста-
нуть рівними, процес треба припинити.
У нашому випадку 5 < 8, отже, продовжуємо процес, але пере-
гляд елементів починаємо праворуч від елемента 45, ліворуч від 67.
36 33 12 18 45 26 15 67 48 89
45 порівнюємо з 15 за раніше встановленою умовою, щоб лі-
вий елемент був менше правого (45 < 15). Умова не виконуєть-
ся, значить, переставляємо елементи. Отримуємо:
36 33 12 18 15 26 45 67 48 89
Після перестановки елементів починаємо порівняння з іншо-
го кінця масиву, тобто з лівого. Порівнюємо 26 із 45 за умовою
26 < 45. Умова виконується, тому рухаємося далі, залишається
один елемент:
45
Перевіряємо умову 45 < 45. Умова не виконується. Порівню-
ємо праворуч, знову отримуємо, що умова 45 < 45 не виконуєть-
ся. Нарешті, перевіряємо останню умову за номерами елементів.
Номери лівого і правого елементів виявляються рівними, оскі-
льки це той самий елемент (7 = 7). Процес припиняється.
Середнім елементом буде елемент під номером 7, рівний 45.
Масив стане таким:
36 33 12 18 15 26 45 67 48 89
Справді, цей елемент задовольняє визначення середнього
елемента. Ліворуч від нього елементи менше 45, а праворуч –
більше 45.
306
Подальший процес сортування ми не будемо поки розбирати
детально, обмежимося лише ідеєю. Масив поділено середнім еле-
ментом на дві частини – ліворуч від нього і праворуч. До кожної з
них (без середнього елемента) застосуємо той самий процес, що
був описаний вище, тобто в кожній частині знайдемо свій середній
елемент; знову отримаємо дві частини, у яких також знайдемо се-
редні елементи і т. д. У підсумку отримаємо впорядкований масив:
12 15 18 26 33 36 45 48 67 89
Цю частину процесу сортування ми розглянемо пізніше, а за-
раз складемо процедуру визначення середнього елемента за за-
пропонованим алгоритмом.
Приклад 2.29. Скласти програму знаходження середнього
елемента масиву, ліворуч від якого розташовуються елементи
менші від нього, а праворуч – більші (з використанням процедур
перестановки елементів і визначення середнього елемента).
Схема процедури. Назвемо процедуру middle (middle з англ.
– середина).
Масив в основній програмі назвемо іменем x.
Procedure middle (вхідним параметром має бути довжина ма-
сиву k: integer; вихідні параметри – масив і номер середнього
елемента);
var
l, r: integer; {Номера лівого та правого елементів}
begin
Треба присвоїти лівому і правому номерам елементів почат-
кові значення 1 та k (довжина масиву).
Основний цикл має тривати до тих пір, поки номери лівого і
правого індексів не стануть рівними між собою (l = r); тоді треба
організувати зовнішній цикл repeat.
Далі йде цикл перегляду елементів, починаючи з правого кі-
нця; він має тривати, поки лівий елемент буде менше або дорів-
нюватиме правому; водночас номер лівого індексу має бути
менше правого. Маємо цикл:
while (x[l] <= x[r]) and (l < r) do
У циклі треба зменшувати індекс правого елемента, рухаю-
чись до середини r: = r-1;
307
Як тільки знайдеться лівий елемент більше правого, треба
зробити їх обмін, для чого звернутися до процедури exchange
(обмін).
Після обміну треба переглядати елементи ліворуч, для чого
організуємо цикл "поки":
while (x[l] <= x[r]) and (l < r) do.
У циклі треба рухатися зліва направо, переглядаючи елемен-
ти, а для цього збільшувати індекс лівого елемента: l:=l+1.
Якщо знаходимо лівий елемент більше правого, то робимо обмін.
Знову звертаємося до процедури обміну.
Зовнішній цикл закінчується умовою: until l = r;
Складемо процедуру обміну значеннями двох елементів ма-
сиву й деталізуємо процедуру пошуку середнього елемента.
Псевдокод Python
Procedure exchange(int l, int r) def exchange(l, r):
Int p; "Swap"
begin p = x[l];
p:=x[l]; x[l] = x[r];
x[l]:=x[r]; x[r] = p;
x[r]:=p;
end def middle(k,m):
Procedure middle(int k, Array x, "sort"
int m) l = 1;
Int l, r; r = k;
begin while True:
l := 1; r := k; while x[l]<=x[r] and l<r:
Repeat r=r-1;
While((x[l]<=x[r]) and (l<r)) exchange(l,r);#Процедура
do r := r-1; обміну
exchange(l,r);{Процедура while x[l]<=x[r] and l<r:
обміну} l=l+1;
While((x[l]<=x[r]) and (l<r)) exchange(l,r);
do l := l+1; if (l == r):
exchange(l,r); break;
Until l = r; m = l;
m := l;
end
308
С++
int x[] = { 50,44,1,234,45 };
void exchange(intl, intr)
{
int p;
p = x[l];
x[l] = x[r];
x[r] = p;
}
void middle(intk, intm)
{
int l, r;
l = 1;r = k;
do
{
while (x[l] <= x[r] && l<r) {r = r – 1;}
exchange(l, r);//Процедура обміну
while (x[l] <= x[r] && l < r) { l = l + 1; }
exchange(l, r);
} while (l == r);
}
С++
int x[] = { 50,44,1,234,45 };
void fast(intq, intp, intx[])
{
int s, l, r;
s = x[l];
do
{
while ((x[r] >= s) && (l < r)) {r=r-1;}
x[l] = x[r];
while ((x[r] <= s) && (l < r)) {l=l+1;}
x[r] = x[l];
} while (l == r);
x[l] = s;
if (q< l – 1) { fast(q, l – 1, x); }
if (l+1 <p) { fast(l+1, p, x); }
}
311
Перший умовний оператор забезпечує рух ліворуч (що ми ро-
збирали на прикладі) і викликає ту саму процедуру – fast, тобто
відбувається її звернення до самої себе. Другий умовний оператор
забезпечує рух праворуч. У ньому також є виклик процедури fast
– виклик самої себе. Отже, ми маємо рекурсивну процедуру.
Приклад 2.31. У масиві є нульові елементи. Скласти програ-
му сортування масиву за зростанням, а потім його стиснення
відкиданням нульових елементів. Нулями заповнюється хвіст
масиву, що звільнився.
Алгоритм
Упорядкування масиву для нас – уже відомий процес. Це
можна зробити за допомогою процедури швидкого сортування,
після чого нульові елементи виявляться на початку масиву. На-
приклад, можливе таке:
0 0 0 0 2 4 6 9 10 12
Тепер треба нульові елементи переставити в кінець масиву.
Отримаємо:
2 4 6 9 10 12 0 0 0 0
Для перестановки нульових елементів у кінець масиву є кіль-
ка способів.
Перший спосіб – підрахувати кількість нульових елементів, а
потім перенумерувати ненульові, починаючи з 1-го і далі за їх-
німи номерами, а решті елементів присвоїти нульові значення.
Другий спосіб – послідовна перестановка нульових елементів
у кінець масиву: беремо перший нульовий елемент і переставля-
ємо в кінець; він виявиться на місці n-го елемента, у нашому
прикладі – на місці десятого:
0 0 0 2 4 6 9 10 12 0
На першому місці знову нульовий елемент. Переставляємо
його, але не на останнє, а на передостаннє місце. Отримуємо:
0 0 2 4 6 9 10 12 0 0
тощо.
Ми скористаємося другим способом, але тепер складемо ре-
курсивну процедуру для перестановки елементів. Звичайно,
можна обійтися без процедури, тим більше рекурсивної, але нам
важливо навчитися використовувати рекурсивні процедури з
масивами.
312
Процедура
С++ Псевдокод
int a[] = { 50,44,1,234,45 }; Procedure swp(int k, int m)
void swp(intk, intm) Int v;
{ Begin
int v; If k = m then goto 1;
if (k == m) { goto stop; } v := a[k];
v = a[k]; a[k] := a[k+1]
a[k] = a[k + 1]; a[k+1] := v;
a[k + 1] = v; swp(k+1,m);
swp(k + 1, m); 1:end
stop:
}
Python
def swp(k,m):
"swap"
while 1:
if(k==m):break;
v = a[k];
a[k] = a[k+1];
a[k+1] = v;
swp(k+1,m);
break;
313
Спочатку слід з'ясувати, для чого потрібна змінна m. Для
цього варто згадати, що перший нульовий елемент переставля-
ється на останнє місце, тоді m має дорівнювати n; другий нульо-
вий елемент має бути переставлений на передостаннє місце,
отже, m має дорівнювати n-1 тощо за кількістю нульових елеме-
нтів. Значення m задається із основної програми, а значення
номера нульового елемента має завжди дорівнювати одиниці,
тому що після кожної перестановки нульового елемента наступ-
ний займає його місце, тобто перебуватиме на місці першого.
Тоді початкове значення k, що задається із основної програми.
дорівнює 1. Щоб процедура не зациклилася на початку, включе-
но умовний оператор, який перевіряє значення k. Якщо воно
дорівнюватиме m, то це буде означати, що нульовий елемент
переставлено і він зайняв своє місце в кінці масиву. Тоді проце-
дуру треба завершити, для чого включено оператор безумовного
переходу goto, що пересилає керування на end.
Для підрахунку кількості нульових елементів на початку впо-
рядкованого масиву в основній програмі є цикл repeat ... ... until, у
якому підраховується кількість нулів і відразу відбувається звер-
нення до процедури перестановки. Однак, оскільки цей цикл за-
кінчується умовою a [d]> 0, то два нульові елементи будуть не
підраховані й не переставлені. Для цього в програму введені ще
два звернення до процедури: swp (1, n- d + 1); swp (1, n- d);
Деталізація програми:
Псевдокод Python
Procedure fast(int q, int p,return x) def swp(k,m):
Int s,l,r; "swap"
begin while 1:
l:=q; r:=p; if(k==m):break;
s:= x[l]; v = a[k];
Repeat a[k] = a[k+1];
While((x[r]>=s) and (l<r)) do a[k+1] = v;
r:=r-1; swp(k+1,m);
x[l]:=x[r]; break;
While((x[l]<=s) and (l<r)) do
l:=l+1; def fast(q,p,x):
x[r]:=x[l]; "sort method"
Until l = r l = q;
314
x[l]:=s; r = p;
if q<l-1 then fast(q,l-1,x); s = x[l];
if l+1<p then fast(l+1,p,x); while True:
end while(x[r] >= s and l<r):
Procedure swp(int k, int m) r=r-1;
Int v; x[l]=x[r];
Begin while(x[l]<=s and l<r):
If k = m then goto 1; l=l+1;
v := a[k]; x[r] = x[l];
a[k] := a[k+1] if (l == r):
a[k+1] := v; break;
swp(k+1,m); x[l]=s;
1:end if (q<l-1):x = fast(q,l-1,x);
Begin if (l+1<p):x = fast(l+1, p,x);
Const n = 5; return x;
For i:=1 to n do
begin if __name__ == '__main__':
write('Уведіть',i,'-й елемент'); n = 5;
readln(a[i]); a = [0]*n;
end for i in range(0,n,1):
fast(1,n,a); a[i] = int(input('Уведіть ' +
d:=1; str(i) + "-й ел. масиву:"));
Repeat a = fast(1,(n-1),a);
swp(1,n-d+1) d=1;
d:=d+1; while True:
Until a[d]>0 swp(1,(n-1)-d+1);
swp(1,n-d+1); d=d+1;
swp(1,n-d); if(a[d]>0):
writeln('Упорядкований масив break;
з нулевими ел. У кінці'); swp(1, (n-1) – d + 1);
for i:=1 to n do write(a[i],' '); swp(1, (n-1) – d);
writeln; print('Упорядкований масив
end з нулевими ел. у кінці');
for i in range(0,n):
print(str(a[i]) + ' ');
С++
void swp(intk, intm, int *a)
{
int v;
if (k == m) { goto stop; }
315
v = a[k];
a[k] = a[k + 1];
a[k + 1] = v;
swp(k + 1, m, a);
stop: cout <<".";
cout << endl;
}
void fast(intq, intp, int *x)
{
int s, l, r,x_r;
l = q; r = p;
s = x[l];
do
{
x_r = x[r];
while ((x_r >= s) && (l < r)) { r = r – 1; }
x[l] = x[r];
x_r = x[r];
while ((x_r <= s) && (l < r)) { l = l + 1; }
x[r] = x[l];
} while (l != r);
x[l] = s;
if (q< l – 1) { fast(q, l – 1, x); }
if (l + 1 <p) { fast(l + 1, p, x); }
}
int main()
{
int n = 5;
int *a = newint[n];
for (int i = 0; i < n; i++)
{
cout <<"a["<< i <<"]:"<< endl;
cin >> a[i];
}
fast(1, n-1, a);
int d = 1;
do
{
swp(1, (n-1) – d + 1,a);
d = d + 1;
} while (a[d] < 0);
swp(1, (n-1) – d + 1,a);
316
swp(1, (n – 1) – d,a);
cout <<"Sorted Array: "<< endl;
for (int i = 0; i < n; i++)
{
cout << a[i] <<" ";
}
cout << endl;
system("pause");
return 0;
}
317
Продовжуємо перегляд масиву a, беремо третій елемент, a [3]
= – 77, і порівнюємо з першим елементом масиву b:
a [3] <b [1], (-77 <-96).
Умова не виконується, значить, наступному, третьому елемен-
ту масиву c, присвоюється значення першого елемента масиву b:
c [3]: = b [1], c [3]: = – 96.
Процес триватиме до тих пір, поки не закінчаться елементи
масиву a або b (незважаючи на те, що кількість елементів маси-
ву a менше, ніж кількість елементів масиву b, це не означає, що
перегляд масиву a закінчиться раніше; може статися навпаки,
швидше закінчиться перегляд масиву b, якщо його елементи
будуть менше елементів масиву a).
Однак будь-коли елементи одного з масивів закінчаться. Тоді
треба елементи одного з масивів присвоїти наступним за поряд-
ком елементам масиву c. Як ми бачили, із останнього факту ви-
пливає, що необхідно брати до уваги елементи як масивів a та b,
так і масиву c. Для цього треба мати змінні лічильники й переві-
ряти, чи не скінчилися елементи масивів a та b ще на початку
процедури, перед порівнянням елементів. У іншому випадку мо-
жна спізнитися: лічильник кількості елементів буде збільшено, а
елементи закінчаться; тоді може бути пропущений один з елеме-
нтів іншого масиву, у якого елементи ще не скінчилися. Вихо-
дячи з цих міркувань, можна скласти таку схему процедури.
1. Установити змінним p та q лічильників елементів масивів a
та b початкові значення 0, а лічильнику елементів масиву c –
змінній k – значення 1. (Будемо позначати кількість елементів
масиву a через n, а кількість елементів масиву b – m, тоді кіль-
кість елементів масиву c буде n + m).
2. Перевірити умову if p = n (тобто елементи масиву a скін-
чилися). Якщо вона виконується, то треба збільшити значення
лічильника елементів іншого масиву на 1 і присвоїти відповід-
ному елементу масиву c значення елемента масиву b: q: = q + 1;
c[k]: = b[q]; потім продовжити процес перегляду елементів ма-
сивів. Повністю рядок програми буде таким:
if p = n then
begin
q:=q+1;
c[k]:=b[q];
goto ... end;
318
3. Перевірити умову if q = m (тобто елементи масиву b скін-
чилися). Якщо вона виконується, то збільшити лічильник еле-
ментів масиву a на 1 і присвоїти відповідному елементу масиву
c значення елемента масиву a:
p:=p+1; c[k]:=a[p].
Після цього продовжити перегляд елементів масиву:
if q = m then
begin
p:=p+1;
c[k]:=a[p];
goto ... end;
4. Записати оператори порівняння елементів масивів і при-
своювання елементам масиву c потрібних значень:
if a[p+1] < b[q+1]
then begin p:=p+1; c[k]:=a[p]; goto ... end
else begin q:=q+1; c[k]:=b[q]; goto ... end;
5. Оскільки процедура рекурсивна, то треба записати звер-
нення до цієї ж процедури.
Щоб процедура не зациклилася, перед початком виконання
операторів процедури треба включити умовний оператор, який
має перевіряти значення лічильника k (якщо k = n + m + 1, то
процедуру закінчити). Зверніть увагу, що порівняння лічильника
виконується не за допомогою n + m, а з використанням n + m +
1. Це викликано тим, що початкове значення k дорівнює 1, а
останнє виконання процедури має бути при k = n + m. Зрозумі-
ло, вона має закінчитися, коли k дорівнюватиме n+m+1.
Деталізація
Псевдокод Python
Procedure new(int n, int m, int q, def new(n,m,q,p,k, c):
int p, int k, return Array c) if(k==n+m+1): goto .1
Begin if(p==n):
If k=n+m+1 then goto 1; q=q+1;
If p=n then c[k]=b[q];
begin goto .2;
q:=q+1; if(q==m):
c[k]:=b[q]; p=p+1;
319
goto 2; c[k] = a[p];
end goto .2;
if q=m then if a[p+1]<b[q+1]
begin p=p+1;
p:=p+1; c[k] = a[p];
c[k]:=a[p]; goto .2;
goto 2; else:
end q=q+1;
if a[p+1]<b[q+1]then c[k]=b[q];
begin label .2
p:=p+1; new(n,m,q,p,k+1,c);
c[k]:=a[p]; label .1
goto 2; print("end of program");
end
else
begin
q:=q+1;
c[k]:=b[q];
end
2: new(n,m,q,p,k+1,c);
1: end
С++
void New(intn, intm, intq, intp, intk, int *c)
{
if (k == n + m + 1) { goto one; }
if (p == n)
{
q = q + 1;
c[k] = b[q];
goto two;
}
if (q == m)
{
p = p + 1;
c[k] = a[p];
goto two;
}
if (a[p + 1] < b[q + 1])
{
320
p = p + 1;
c[k] = a[p];
goto two;
}
else
{
q = q + 1;
c[k] = b[q];
}
two: New(n, m, q, p, k + 1, c);
one: cout <<"end of program"<< endl;
}
322
q:=q+1; if (l+1<p):fast1(l+1, p,x);
c[k]:=b[q];
end
2: new(n,m,q,p,k+1,c);
1: end if __name__ == '__main__':
Begin for i in range(0,n,1):
Randomize a[i] = random.randint(10,
For i:= 1 to n do 100);
a[i]:= random(201)-100; for i in range(0,m,1):
For i:= 1 to m do b[i] = random.randint(10,
b[i]:= random(201)-100; 100);
fast(1,n,a); fast(1,n-1,a);
writeln('Задано впорядкований print('1 Array :a');
1-й масив'); for i in range(0, n, 1):
For i:=1 to n do write(a[i],''); print(a[i]);
Writeln; print('');
fast1(1,m,b); fast1(1,m-1,b);
writeln('Задано впорядкований print('2 Array :b');
2-й масив'); for i in range(0, m, 1):
For i:=1 to n do write(b[i],''); print(b[i]);
Writeln; print('');
new(n,m,0,0,1,c); new(n-1,m-1,0,0,1,c);
writeln('Новий упорядкований print('3 Array :c');
об'єднаний масив'); for i in range(0, (n-1)+(m-1), 1):
For i:=1 to n+m do write(c[i],' '); print(c[i]);
Writeln; print('');
End
С++
void New(intn, intm, intq, intp, intk, int *c, int *a, int *b)
{
if (k == n + m + 1) { goto one; }
if (p == n)
{
q = q + 1;
c[k] = b[q];
goto two;
}
if (q == m)
{
323
p = p + 1;
c[k] = a[p];
goto two;
}
if (a[p + 1] <b[q + 1])
{
p = p + 1;
c[k] = a[p];
goto two;
}
else
{
q = q + 1;
c[k] = b[q];
}
two: New(n, m, q, p, k + 1, c,a,b);
one: cout <<".";
}
int n = 5;
int m = 10;
int *a = newint[n];
int *b = newint[m];
int *c = newint[n + m];
324
fast(q, r);
if (p> l)
fast(l, p);
}
void fast1(intq, intp)
{
int l = q, r = p;
int piv = b[l + (r – l) / 2];
while (l <= r)
{
while (b[l] < piv)
l++;
while (b[r] > piv)
r–;
if (l <= r)
swap(b[l++], b[r–]);
}
if (q< r)
fast(q, r);
if (p> l)
fast(l, p);
}
int main()
{
int num;
fast(0, n – 1);
cout <<"1 Array a:"<< endl;
325
for (int i = 0; i < n; i++)
{
cout << a[i] <<' ';
}
cout << endl;
fast1(0, m – 1);
cout <<"2 Array b:"<< endl;
for (int i = 0; i < m; i++)
{
cout << b[i] <<' ';
}
cout << endl;
New(n-1, m-1, 0, 0, 0, c,a,b);
cout <<"3 Array c:"<< endl;
for (int i = 0; i < m+n; i++)
{
cout << c[i] <<' ';
}
cout << endl;
system("pause");
}
326
Процедура
Псевдокод Python
Procedure new(int n, int m, int q, int @with_goto
p, int k, return Array c) def new(n,m,q,p,k, c):
begin if(k==n+m+1): goto .one;
if (k=n+m+1) then goto 1; while((p<n) and (q < m)):
while (p<n)and(q<m) do if(a[p+1]<b[q+1]):
begin p=p+1;
if(a[p+1]<b[q+1] then c[k]=a[p];
begin goto .two;
p:=p+1; else:
c[k]:=a[p] q=q+1;
goto 2; c[k]=b[q];
end goto .two;
else if(p==n):
begin q=q+1;
q:=q+1; c[k]=b[q];
c[k]:=b[q]; goto .two;
goto 2; if(q==m):
end p=p+1;
end c[k]=a[p];
if(p=n)then label .two
begin new(n,m,q,p,k+1,c);
q:=q+1;c[k]:=b[q];goto 2; label .one
end
if(q=m)then
begin
p:=p+1;c[k]:=a[p]
end
2:new(n,m,q,p,k+1,c);
1:end
С++
void New(intn, intm, intq, intp, intk, int *c)
{
if (k == n + m + 1) { goto one; }
while ((p<n)&&(q<m))
{
if (a[p + 1] < b[q + 1])
{
p = p + 1;
c[k] = a[p];
goto two;
327
}
else
{
q = q + 1;
c[k] = b[q];
goto two;
}
}
if (p == n) {
q = q + 1;
c[k] = b[q];
goto two;
}
if(q==m)
{
p = p + 1;
c[k] = a[p];
}
two:New(n, m, q, p, k + 1, c);
one:cout <<"EOF"<< endl;
}
328
Алгоритм
Алгоритм розв'язання дуже простий. Береться перший еле-
мент масиву і порівнюється з попереднім (якщо він дорівнює
попередньому, то порівняння продовжується далі; якщо не дорі-
внює, то виводиться на екран), а оскільки попереднього немає,
то й рівного йому немає, а це означає, що він виводиться на ек-
ран як той, що не має рівних.
Далі береться другий елемент і таким саме чином порівню-
ється з попередніми. Якщо він дорівнює першому елементу, то
береться третій елемент і порівнюється з першим; якщо йому
немає рівного, то він виводиться на екран. Якщо рівні йому є, то
береться наступний елемент, четвертий, і почергово порівню-
ється з першим, другим, третім. Як тільки знаходиться йому
рівний, процес його порівняння з іншими елементами припиня-
ється й береться наступний, п'ятий елемент для порівняння з
першим, другим, третім, четвертим. Якщо знаходиться хоча б
один рівний, то береться наступний елемент – шостий і т. д.
Порівняння елементів можна виконувати одразу після їх уве-
дення з клавіатури. Наприклад:
Псевдокод
Program Problem1;
Int n = 20;
Int array[1..n] t;
Int array[1..n] a;
Int I, j, k;
begin
for i:=1 to n do
begin
write('Enter ', i, ' element '); readln(a[i]);
for j:=1 to i- 1 do if a[i] = a[j] then goto 1;
writeln(a[i]);
1: end
end.
Python C++
N = 10 int main()
flag = False {
array = [] const int N = 10;
329
for i in range(0, 9): int *array = new int[N];
array.append(int(input("Enter bool flag = false;
element: "))) for (int i = 0; i<N; i++){
for j in range(0,i): cout << "\nEnter " << i + 1 << "
if(array[i] == array[j]): value: ";
flag = True cin >> array[i];
if(flag != True):
print(array[i]) for (int j = 0; j<i; j++){
flag = False if (array[i] == array[j])
flag = true;
}
if (!flag)
cout << array[i];
flag = false;
}
return 0;
}
Псевдокод
Program Problem1a;
Int n = 20;
Int array[1..n]t;
Int array[1..n] a;
Int I, j, k;
begin
k:=0;
for i:=1 to n do
begin
write('Enter ', i, ' element '); readln(x);
for j:=1 to k doif x = a[j] then goto 1;
writeln(x);
330
k:=k+1;
a[k]:=x;
1: end;
writeln(k)
end.
Python C++
k=0 int main()
N = 10 {
flag = False int k = 0;
array = [] const int N = 10;
for i in range(0, 9): int *array = new int[N];
x = int(input("Enter element: ")) bool flag = false;
for j in range(0,k): int x = 0;
if(x == array[j]):
flag = True for (int i = 0; i<N; i++)
if(flag != True): {
print(x) cout << "\nEnter " << i + 1 << "
k=k+1 value: ";
array.append(x) cin >> x;
flag = False for (int j = 0; j<=k; j++)
print(k) {
if (x == array[j])
flag = true;
}
if (!flag)
{
cout << x;
k++;
array[k] = x;
}
flag = false;
cout << "\n" << k;
}
return 0;
}
331
Бачимо, що значення, яких набувають змінні k та i, на кож-
ному етапі виконання програми зв'язані нерівністю k ≤ i . Якщо
до закінчення програми виконано k < n, то це означає, що змінні
a[k+1], a[k+2], ..., a[n] не отримали в ході виконання програми
ніяких значень.
Іноді в задачах, схожих на останню, корисно розглянути до-
датковий масив b[1], b[2], ...; число b[i] може, наприклад, пока-
зувати, скільки разів серед вихідних даних зустрічається зна-
чення, що дорівнює a[i].
Приклад 2.34. Нехай є масив цілих чисел a1, ..., an, де n – деяка
константа, і нехай потрібно вивести кожне число один раз, указавши
при цьому, скільки разів воно зустрічається серед a1, ..., an.
Будемо враховувати розв'язання попередньої задачі, але при
цьому розглянемо додатковий масив b[1], ..., b[n]. Маємо таку
схему:
ProgramProblem2;
опис;
begin
k:=0;
fori:=1 tondo
begin
введення елементів масиву
якщо x збігається з деяким a[j], 1 <= j <= k,
то збільшити b[j] на 1 і перейти до мітки 1;
k:=k+1; a[k]:=x; b[k]:=1;
1: end;
вивести a[1], b[1], a[2], b[2], ..., a[k], b[k]
end.
Програма
Псевдокод
Program Problem2;
Int n = 20;
Int array[1..n]t;
Int array[1..n] a;
Int array[1..n] b
Int i, j, k, x;
begin
332
k:=0;
for i:=1 to n do
begin
write('Enter', i, 'element '); readln(x);
for j:=1 to k do
if x = a[j] then
begin
b[j]:=b[j]+1;
goto 1
end;
k:=k+1;
a[k]:=x;
b[k]:=1;
1: end;
for i:=1 to k do writeln(a[i], ' ', b[i])
end.
Python C++
Псевдокод
Procedure repetition(Int n; Array a, Array b; return Int k)
Int i, j;
begin
k:=0;
for i:=1 to n do
begin
for j:=1 to k do
if a[i] = a[j] then
begin
b[j]:=b[j]+1;
goto 1
end;
k:=k+1;
a[k]:=a[i];
b[k]:=1;
1: end;
end;
Python C++
def repetition(n, a, b, k): void Repetition(int n, int* a, int* b,
k=0 int& k)
flag = False {
for i in range(0,n): k = 0;
334
for j in range(0,k): bool flag = false;
if(a[i] == a[j]): for (int i = 0; i < n; i++)
b[j] = b[j] + 1 {
flag = True for (int j = 0; j <= k + 1; j++)
if(flag != True): {
k=k+1 if (a[i] == a[j])
a[k] = a[i] {
b[k] = 1 b[j] = b[j] + 1;flag = true;
return k }
if (!flag)
{
a[k] = a[i];b[k] = 1;k++;
}
}
flag = false;
}
}
335
Далі беремо наступне по порядку не підкреслене число і під-
креслюємо всі числа, кратні йому (що діляться на нього), і т. д.
Таким чином, ми підкреслимо всі складні числа, а прості зали-
шаться не підкресленими:
2, 3, 4 , 5, 6 , 7, 8 , 9 , 10 , 11, 12 , 13, ...
− − − − − −
За цим принципом складемо програму, тільки замість підкре-
слення будемо заміняти складні числа нулями.
Математичний аналіз розв'язання та алгоритм. Ідея розв'я-
зання зрозуміла із історії її виникнення, але одразу постають
деякі запитання.
1. Нехай у нас n чисел. За принципом Ератосфена береться
число 2 і закреслюються парні числа; далі береться 3 і закрес-
люються кратні трьом; незакресленим лишається 5 (4 закресле-
но, оскільки воно парне). Закреслюються всі кратні 5 тощо.
До якого ж граничного числа треба брати ненульові числа?
Якщо всього чисел 100, то треба перевіряти числа до 100 або до
97 (це останнє просте число між 2 та 100)?
Щоб відповісти на це запитання, згадаємо, що дільники будь-
якого цілого числа m, якщо такі взагалі є, містяться у проміжку
від 2 до кореня квадратного з числа m , не враховуючи 1 і
саме число. Звідси нескладно зробити висновок, що якщо задані
числа до 100, то достатньо брати для перевірки числа з проміж-
ку від 2 до 100, тобто до 10, адже найбільше число в цій послі-
довності 100, а значить, навіть найбільший дільник, якщо він
існує, може бути 10.
Це дає нам можливість обмежити цикл перевірки чисел від 2
до sqrt(n), до кореня квадратного з n.
2. А якщо корінь квадратний із числа n не добувається наці-
ло? Наприклад, для 200? Відповідь зрозуміла! Треба округлити
наближене значення кореня до найближчого цілого числа, мен-
шого від sqrt (n). У даному випадку виходить 14,142135 .... Тре-
ба округлити до 14. Тут виникає інша проблема, адже sqrt (n)
має тип дійсний (real), а якщо організовано цикл for, то за гра-
ничне значення для змінної циклу ми можемо взяти тільки ціле
число, дійсні числа в цьому циклі неприпустимі.
336
Ви вже знаєте, що для виконання такої операції в Pythonі є
вбудована функція trunc, яку ми застосуємо для складання про-
грами. Отже, треба організувати цикл for від 2 до trunc (sqrt (n))
(зважте, наскільки менше вже стало роботи для комп'ютера!).
Що ж виконувати в циклі? Знову звернемося до прикладу:
337
якогось проміжку, тоді нам не потрібні нульові елементи, а го-
товий масив простих чисел знадобиться. Отже, відкидаємо ну-
льові елементи та створюємо новий масив тільки із простих чи-
сел. Залишається вивести його на екран, що дуже легко зробити!
Виходить алгоритм. Можливі інші варіанти, якщо у вас з'яв-
ились ідеї, то реалізуйте їх, а ми продовжимо складання схеми
програми, дотримуючись наведених вище міркувань.
Схема програми:
Program Problem3;
опис: константа, що дорівнює кількості чисел;
два масиви однакового розміру;
інші змінні;
begin
цикл, що створює масив натуральних чисел від 1 до n;
змінній f присвоюється значення, що дорівнює
f:=trunc(sqrt(n));
цикл від 2 до f: for i:=2 to f do
у циклі, якщо a[i] <> 0, то змінній b присвоїти значення цьо-
го елемента й організувати цикл "while ", у якому перевірити
ділення ненульових елементів масиву на b;
якщо елемент ненульовий і ділиться на b, то присвоїти йому
значення 0, збільшити значення змінної циклу на величину b і
продовжити цикл; інакше, якщо елемент дорівнює нулю, то
збільшити значення змінної циклу на b і продовжити цикл до n;
закінчити цикл "while";
закінчити цикл for;
змінній-лічильнику простих чисел присвоїти початкове зна-
чення 0; організувати цикл for, у якому елементам нового маси-
ву присвоїти значення ненульових елементів, тобто простих
чисел;
вивести на екран масив простих чисел і кількість простих
чисел;
end.
Деталізація. Деталізацію почнемо не з опису змінних, а зі
складання програми. Опис змінних і масивів виконаємо потім.
1. Цикл, що створює масив цілих чисел від 1 до n:
for i:=1 to n do a[i]:=i;
338
2. Установити змінній f граничне значення:
f:=trunc(sqrt(n));
3. Цикл від 2 до f: for i:=2 to f do
у циклі, якщо a[i] <> 0, тоді змінній b присвоїти значення
цього елемента і організувати цикл "while", у якому перевірити
ділення ненульових елементів масиву на b;
for i:=2 to f do
begin
if a[i] <> 0 then
begin
b:=a[i];
j:=b*2;
while j <=n do
begin
Зазначимо, що в програмі можна легко обійтись без змінної
b, а зробити так: j:=a[i]*2. Змінну b уведено для зручності.
4. Якщо елемент ненульовий і ділиться на b, то присвоїти
йому значення 0, збільшити значення змінної циклу на величину
b і продовжити цикл; інакше, якщо елемент дорівнює нулю, то
збільшити значення змінної циклу на b і продовжити цикл до n;
закінчити цикл "while";
begin
if a[j] mod b = 0 then a[j]:=0;
j:=j+b
end
else
j:=j+b
end
Закінчити цикл for; end
6. Змінній-лічильнику простих чисел присвоїти початкове
значення 0;
організувати цикл for, у якому елементам нового масиву
присвоїти значення ненульових елементів, тобто простих чисел;
k:=0;
for i:=2 to n do
if a[i] <> 0 then
339
begin
k:=k+1;
c[k]:=a[i]
end;
7. Вивести на екран масив простих чисел і кількість простих
чисел;
writeln('Прості числа з проміжку [2, ', n, ']');
for i:=1 to k do write(c[i], ' ');
writeln;
writeln('Усього простих чисел із цього проміжку ', k)
8. Тепер можна виконати опис змінних і масивів:
const
n = 100;
type
t = array[1..n] ofinteger;
var
a, c: t;
i, j, b, f, k: integer;
Можна скласти окрему процедуру "Решето Ератосфена", що
дасть можливість використовувати її в інших програмах:
Procedure eratosfen(n: integer; a: t; var c: t; var k: integer);
var
f, i, j, b: integer;
begin
f:=trunc(sqrt(n));
for i:=2 to f do
begin
if a[i] <>0 then
begin
b:=a[i];
j:=b*2;
while j <= n do
begin
if a[j] <> 0 then
begin
if a[j] mod b = 0 then a[j]:=0;
340
j:=j+b
end else j:=j+b
end
end
end;
k:=0;
for i:=2 to n do
if a[i] <> 0 then
begin
k:=k+1;
c[k]:=a[i]
end;
end;
341
Четверте просте число 7, йому передують або дорівнюють
чотири числа: 2, 3, 5, 7. Перевіряємо кожне з них у сумі із 7.
2 + 7 = 14, умова не виконується; 3 + 7 = 14, умова не виконується;
5 + 7 = 14, умова не виконується; 7+7 = 14, умова виконується.
Отже, ці прості числа потрібно вивести на екран.
Можна було б цим обмежитися, якщо б перед нами стояло
завдання просто з'ясувати, чи можна певне число зобразити у
вигляді суми двох простих чисел. Ми можемо відповісти впев-
нено і закінчити процес. Однак ми хочемо знати, чи є ще спосо-
би зображення парного числа у вигляді суми двох простих чи-
сел. Тому процес перевірки варто продовжити. Беремо наступне
просте число – 11. Для нього попередніми є: 2, 3, 5, 7, 11.
Перевіряємо: 2+11 = 14, умова не виконується; 3+11= 14,
умова виконується, отже, і цей варіант зображення числа 14 у
вигляді суми двох простих чисел варто вивести на екран і про-
довжити перевірку.
5+11 = 14, умова не виконується; 7+11 = 14, умова не вико-
нується; 11 + 11 = 14, умова не виконується.
Наступне просте число 13. Перевіряємо його в сумі з попере-
дніми або рівним йому.
2+13 = 14, умова не виконується; 3+13 = 14, умова не викону-
ється; 5+13 = 14, умова не виконується; 7+13 = 14, умова не ви-
конується; 11+13=14, умова не виконується; 13+13 = 14, умова
не виконується.
Усі прості числа перевірені, перевірку потрібно закінчити.
Розв'язання задачі знайдено: 14 може бути зображено у вигляді
суми двох простих чисел. Знайдено також способи зображення
цього числа, їх два: 7+7 та 3+11.
Чому ж для перевірки обраного простого числа беруться
тільки менші або рівне йому прості числа? Адже можна було б
перевірити обране просте число, складаючи його з усіма прос-
тими числами, меншими за задане парне число, тобто з усіма
можливими простими числами для даного випадку. Однак, якщо
піти таким шляхом, то неминучі повторення. Перевірте!
Тепер можна скласти алгоритм розв'язання, тобто написати під-
програму-процедуру, що визначає прості числа за методом Ерато-
сфена, вхідними параметрами якої будуть: задане парне число, до
якого треба знаходити прості числа від 2 до n, де n – задане парне
число; другим параметром є числовий масив від 1 до n.
342
Вихідні параметри: числовий масив c – тільки прості числа;
k – кількість елементів масиву.
В основній програмі, після введення користувачем числа і ви-
клику процедури простих чисел, потрібно організувати цикл за
кількістю елементів масиву простих чисел від 1 до k. Усередині
циклу зробити ще один цикл із параметром j від 1 до i (де i –
змінна зовнішнього циклу), у якому перевіряти суму простих
чисел c[i]+c[j] і порівнювати її із заданим парним числом. Якщо
сума дорівнює йому, то відповідну інформацію вивести на екран.
Схема програми:
Program Problem4;
опис змінних і процедури визначення простих чисел;
begin
write(Введіть парне число, що більше або дорів-
нює 4, ');
write('Менше або дорівнює ', n); readln(m);
eratosfen(m, a, c, k);
write('Число ', m, ' можна зобразити у вигляді суми ');
writeln('двох простих чисел ');
for i:=1 to k do
for j:=1 to i do
if c[i]+c[j] = m then writeln(c[i], '+', c[j]);
end.
Після необхідної деталізації отримаємо:
Псевдокод
Program Problem4;
Int n = 1000;
Int array a[1..n];
Int array c[1..n];
Int i, j, k;
Procedure eratosfen(Int n,Array a,Array c,Int k);
Int f, i, j, b;
begin
f:=trunc(sqrt(n));
for i:=2 to f do
begin
if a[i] <> 0 then
343
begin
b:=a[i];
j:=b*2;
while j <= n do
begin
if a[j] <> 0 then
begin
if a[j] mod b = 0
then a[j]:=0;
j:=j+b
end
else
j:=j+b
end
end
end;
k:=0;
for i:=2 to n do
if a[i] <> 0 then
begin
k:=k+1;
c[k]:=a[i]
end;
end;
begin
write('Enter number more or equal 4, ');
write('but less than ', n, ' '); readln(m);
for i:=1 to n do a[i]:=i;
eratosfen(m, a, c, k);
write('Number', m, ' is representable as sum of');
writeln('two prime numbers ');
for i:=1 to k do
for j:=1 to i do
if c[j]+c[i] = m then writeln(c[j], '+', c[i])
end.
Python C++
import math void eratosfen(int n, int* a, int* c,
int& k){
def eratosfen(n, a, c, k): int f, b, j, i;
344
f = math.trunc(math.sqrt(n)) f = trunc(sqrt(n));
for i in range(1,f): for(i = 1; i < f; i++)
if(a[i] != 0): {
b = a[i] if(a[i] != 0)
j=b*2 {
while(j <= n): b = a[i];
if(a[j – 1] != 0): j = b * 2;
if(a[j – 1] % b == 0): while(j <= n)
a[j – 1] = 0 {
j=j+b if(a[j – 1] != 0)
else: {
j=j+b if (a[j – 1] % b == 0)
k=0 a[j – 1] = 0;
for i in range (1,n): j = j + b;
if(a[i] != 0): }
c.append(a[i]) else
k=k+1 {
return k; j = j + b;
}
N = 1000 }
array = [] }
array2 = [] }
m=0 k = 0;
k=0 for(i = 1; i < n; i++)
m = int(input("Enter number more {
or equal 4 but less 1000: ")) if (a[i] != 0)
for i in range(1,N): {
array.append(i) c[k] = a[i];
k = eratosfen(m, array, array2, k) k++;
print("Number is representable as a }
sum of 2 prime numbers") }
for i in range(0,k): }
for j in range(0, i + 1):
if(array2[j] + array2[i] == m): int main()
print(str(array2[j]) + " " + {
str(array2[i])) const int N = 1000;
int* array = new int[N];
int* array2 = new int[N];
int m, i = 0;
int k = 0;
345
cout << "Enter number more or
equal 4 but less than 1000: ";
cin >> m;
for(i = 0; i < N; i++){
array[i] = i + 1;
}
Python C++
def gorner(k, x, a): double gorner(int k, double* a) {
if(k == 1): if(k = 1)
gorner = a[1] gorner = a[1];
347
else: else
gorner = gorner(k-1,x,a) * x + a[k] gorner = gorner(k-1,x,a) * x + a[k];
}
Вправи
1. Скласти програму, після виконання якої з'ясується, чи є се-
ред a1, a2, ..., an хоча б одна пара збіжних за величиною елемен-
тів.
2. Написати програми, після виконання яких у масиві a1, a2,
..., an визначається кількість сусідств:
а) двох додатних чисел;
б) двох чисел різного знака;
в) двох чисел одного знака, причому абсолютна величина
першого числа має бути більше за друге число.
3. Два непарні прості числа, що відрізняються на два, нази-
вають близнюками. Близнюками є, наприклад, числа 5 і 7, 11 і
13, 17 і 19 тощо. На початку натурального ряду такі числа зу-
стрічаються досить часто, але що більше ми просуваємося в
область великих чисел, то менше й менше їх стає. Відомо, що в
першій сотні є 8 близнюків, далі вони розміщені дуже нерівно-
мірно й ми знаходимо їх усе рідше, набагато рідше, ніж прості
числа. Досі неясно, чи кінцева кількість близнюків, до того ж ще
не знайдено способу, за допомогою якого можна було б розв'я-
зати цю проблему. Напишіть програму, яка буде знаходити всі
числа-близнюки в інтервалі [2; 1000]. Використовуйте для зна-
ходження простих чисел процедуру "Решето Ератосфена".
4. Складіть функцію, яка буде перевіряти, чи є числа, що міс-
тяться по обидва боки від заданого парного числа, близнюками.
5. При розв'язанні завдань Гольдбаха і Виноградова були
складені програми, що перевіряють їх правильність для одного
парного або непарного числа. Змініть і доповніть програми так,
щоб вони розв'язували ці питання для всіх парних або непарних
чисел із заданого проміжку.
6. Розрахувати значення багаточлена
z = 2x12 − 4,5x10 + x9 + 3x7 − 0,5x4 − x2 +1,
використовуючи формулу Горнера.
348
7. Розрахувати значення полінома
z = x 8 + 2 x 7 + 3 x 6 + 4 x 5 + 5 x 4 + 6 x 3 + 7 x 2 + 8 x + 9.
Вказівка. Оскільки коефіцієнти полінома – числа натурально-
го ряду, то зводити їх у масив не має сенсу. Обчислення їх доці-
льно виконувати в процесі розв'язання. Тоді формула для обчис-
лення поточного значення полінома матиме вигляд
z n = z n −1 ⋅ x + n.
8. Розрахувати значення s = (1 + x)8 , використовуючи форму-
лу Горнера:
1 2 3
s = ... ⋅ x + 1 ⋅ ⋅ x + 1 x + ... + 1 ⋅ 8 ⋅ x + 1.
8 7 6
x2 x12
9. Розрахувати суму членів ряду z = 1 + x + + ... + , вико-
2! 12!
ристовуючи формулу Горнера:
x x x
z = ... + 1 ⋅ + 1 ⋅ + ... + 1 ⋅ x + ... + 1 ⋅ x + 1.
12 11 10
349
6. Дано масив A[N, M]. Знайти максимальну суму елементів
прямокутного підмасиву за всіма можливими прямокутними
підмасивами.
7. Складіть матрицю A(m, n) так, щоб елементи її більшої
діагоналі дорівнювали нулю.
8. Дано матрицю
b11 b12
b21 b22 .
... ...
b91 b92
Написати програму побудови матриці з тими самими
розмірами, елементи i-го рядка якої дорівнюють bi1 + bi 2 та
bi 2 × bi 2 , відповідно.
9. Складіть програму обчислення добутків елементів такої
матриці, розташованих по двох основних діагоналях:
4 6 7 3 4
12 17 9 6 8
3 1 4 9 10 .
6 5 6 11 7
4 9 5 8 17
350
a11 a12 ... a1n
a21 a22 ... a2 n
... ... ... ...
an1 an 2 ... ann
знайти:
а) суму всіх елементів;
б) суму діагональних елементів (елементів з рівними
значеннями обох індексів);
в) суму позадіагональних елементів;
г) значення найбільшого й найменшого з діагональних
елементів.
12. Написати програму, у результаті виконання якої
з'ясується, які з трійок a1i, a2i, a3i (i = 1, 2, ..., 17) можуть служити
сторонами трикутника. Нехай буде виведений масив b1, b2, ...,
b17, що складається з нулів і одиниць. Якщо bi = 1, то a1i, a2i, a3i
можуть бути сторонами трикутника; якщо bi = 0, то ні.
13. Дано цілі числа n, m і вектори a1, a2, ..., am; b1, b2, ...,bn.
Побудувати матрицю, елементи якої визначаються aj рівностями
aj
cij = , де і = 1, 2, ..., n; j = 1, 2, ..., m.
1− | bi |
14. Дано натуральне число n. Написати програму побудови
матриці порядку n:
1 0 ... 0
0 1 ... 0
... ... ... ...
0 0 0 1
(на діагоналі – одиниці, поза діагоналлю – нулі).
15. Дано натуральне число n. Написати програму побудови
матриці порядку n:
n 0 ... 0
n −1 n ... 0
... ... ... ...
1 2 ... n
(над діагоналлю стоять нулі).
351
16. Дано масив a1, a2, a3, ..., an. Написати програму побудови
матриці порядку n:
a1 a2 a3 ... an − 2 an −1 an
a2 a3 a4 ... an −1 an a1
... ... ... ... ... ... ...
.
an − 2 an −1 an ... an −5 an − 4 an −3
a an a1 ... an − 4 an −3 an − 2
n −1
an a1 a2 ... an −3 an − 2 an −1
17. Сформувати з масиву a[1], a[2], ..., a[p] квадратну
матрицю порядку n. Відомо, що p = n × n . Елементи a[1],
a[2], ..., a[n] мають утворювати перший рядок, елементи a[n+1],
a[n+2], ..., a[2 × n] – другий рядок і т. д. Скласти програму.
2.9.8. Множини
Псевдокод
Type number=set of 0..22;
С++
set<int> mySet;
for (int i = 0; i <= 22; i++)
mySet.insert(i);
Python
a = set(range(23))
Псевдокод
SET OF тип
352
С++
set<> тип
Python
set() функція, що повертає тип
Псевдокод
Type T1=(jan, feb, mar, apr, jun, jul, aug, sep, oct, nov, dec);
Month= set of T1;
Var m: Month;
Mm: T1;
С++
typedefenum {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov,
Dec} Months;
set<Months> m;
Months Mm;
Python
Mm = Enum('Month', 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov
Dec')
m = set(Months)
#або
classMonth(Enum):
Jan=1;Feb=2;Mar=3;Apr=4;May=5;Jun=6;Jul=7;Aug=8;Sep=9;Oct=10;
Nov=11;Dec=12
m = set(Months)
353
С++
m = { Feb, Mar, Apr, Jun };
m = {};
Python
m = {Month.Feb, Month.Mar, Month.Apr, Month.Jun}
m = {}
операція різниці:
C++ Python
vector<type> a, b, c; a.difference(b,..)
set<type> c2; a-b-..
…
set_difference(a.begin(), a.end(),
b.begin(), b.end(), back_inserter(c));
c2.insert(c.begin(), c.end());
операція перетину *:
C++ Python
vector<type> a, b, c; a.intersection(b,..)
set<type> c2; a&b&..
…
set_intersection(a.begin(), a.end(),
b.begin(), b.end(), back_inserter(c));
c2.insert(c.begin(), c.end());
354
операція еквівалентності =:
C++ Python
a==b a==b
a.isdisjoint(b)
355
C++ Python
a.find(el) !=a.end() elin a
– результат false.
Псевдокод
[feb,dec]<=(m+dec)
С++
vector<Months> a, b, c, f, result;
set<Months> result2, c2, f2;
f = { Feb, Dec };
a = { Feb, Mar, Apr };
b = { Dec };
f2.insert(f.begin(), f.end());
set_union(a.begin(), a.end(), b.begin(), b.end(),
back_inserter(c));
c2.insert(c.begin(), c.end());
set_intersection(c.begin(), c.end(), f.begin(), f.end(),
back_inserter(result));
result2.insert(result.begin(), result.end());
f2 == result2
Python
print({Month.Feb,Month.Dec}<=(m|set({Month.Dec})))
356
Результат – true, тому що всі елементи множини, яка містить-
ся ліворуч, є підмножиною множини, що міститься праворуч.
C++
set<char> s1, s2, s3;
set<int> s4, s5, s6;
s1 = { '1','2','3' };
s2 = { '3','2','1' };
s3 = { '2', '3' };
for (int i = 0; i <= 3; i++)
s4.insert(i);
s4.insert(6);
s5 = { 4, 5 };
for (int i = 3; i <= 9; i++)
s6.insert(i);
Python
s1 = set('123')
s2 = set('321')
s3 = set('23')
s4 = set(range(0, 4))|set(range(6,7))
s5 = { 4, 5 };
s6 = set(range(3,10));
357
Мова Операція Семантика Приклад Результат
Псевдо * s1*s3 ['2', '3']
Перетин
С++ відсутня –| |– –| |–
множин
Python & s1&s3 {'2','3'}
Псевдо + s4+s5 [0. .6]
Об'єднання
С++ відсутня –| |– –| |–
множин
Python | s4|s5 {0,..,6}
Псевдо - s2-s3 ['1']
Різниця
С++ відсутня –| |– –| |–
множин
Python - s2-s3 {'1'}
s1=s2 True
Псевдо =
s1=s3 False
True,
якщо множини s1==s2 1
С++ ==
s1==s3 0
еквівалентні
s1==s2 True
Python ==
s1==s3 False
s1<>s2 False
Псевдо <>
s1<>s3 True
True,
s1!=s2
С++ != якщо множини
не еквівалентні s1!=s3
s1!=s2
Python !=
s1!=s3
Псевдо <= True, якщо s5<=s6 True
С++ відсутня друга множина –| |– –| |–
Python <= містить першу s5<=s6 True
Псевдо >= True, якщо s2>=s3 True
С++ відсутня перша множина –| |– –| |–
Python >= містить другу s2>=s3 True
Псевдо In Перевірка 7-5 In s4 True
приналежності
відсутня, по-
С++ значення виразу, –| |– –| |–
яснення нижче
який стоїть ліво-
відсутня, по- руч, множині, яка
Python –| |– –| |–
яснення нижче стоїть праворуч
359
7. Дано три множини: X1 = {1,2,3, ..., 20}, X2 = {10,20, ...,
190,200} та X3 = {10,11,12, ... 40}. Сформувати множину Y = (X2
X3) \ ((X1∩X2) \ (X1∩X3)) і множину Y1, що складається з еле-
ментів Y, поділених на 2. Якщо отримане в результаті поділу
число не ціле, то округлити його до найближчого цілого. На
друк вивести Y та Y1. Початкові множини ввести з клавіатури.
8. Дано три множини: X1 = {T2, T4, T6, T8, T10}, X2 = {T1,
T2, T3, T4, T5} та X3 = {T2, T3, T5, T7, T8}. Сформувати мно-
жину Y = (X2 \ X3) ∩ (X1 \ X3). На друк вивести Y та її потуж-
ність. Початкові множини описати як типізовані константи.
9. Розробити програму для визначення, яким алфавітом (ла-
тиницею чи кирилицею) уведено з клавіатури символ. На друк
вивести введений символ з коментарем, наприклад: Символ "А"
набраний кирилицею.
360
ЛІТЕРАТУРА
1. Доусон М. Программируем на Python / М. Доусон. – СПб. :
Питер, 2014.
2. Лутц М. Изучаем Python / М. Лутц. – 4-е изд. ; пер. с англ.
– СПб. : Символ-Плюс, 2011.
3. Лутц М. Программирование на Python / М. Лутц. – 4-е
изд. ; пер. с англ. – СПб. : Символ-Плюс, 2011. – Т. I
4. Лутц М. Программирование на Python / М. Лутц. – 4-е
изд. ; пер. с англ. – СПб. : Символ-Плюс, 2011. – Т. II
5. Керниган Б. В. Язык программирования С / Б. В. Керниган,
Д. М. Ричи. – 2-е изд. – М. : Изд. дом "Вильямс", 2009.
6. Компиляторы. Принципы, технологии, инструментарий
/ А. В. Ахо, М. С. Лам, Р. Сети, Д. Д. Ульман. – 2-е изд. – М. :
Изд. дом "Вильямс", 2008.
7. Вирт Н. Алгоритмы + структуры данных = программы
/ Н. Вирт. – М. : Мир, 1985.
8. Дал У. Структурное программирование / У. Дал,
Е. Дейкстра, К. Хоор. – М. : Мир, 1975.
9. Візуалізатор online на Python [Електронний ресурс]. – Режим
доступу : http : // pythontutor . com / visualize . html # mode = edit
10. Завдання на Python на основі рейтингу [Електронний ре-
сурс]. – Режим доступу : https://www.hackerrank.com
11. Федоров Д. Ю. Программирование на языке высокого
уровня Python : учеб. пособ. для прикладного бакалавриата
/ Д. Ю. Федоров. – М. : Изд-во Юрайт, 2018. – (Серия : Бакалавр.
Прикладной курс).
12. Свейгарт Е. Учим Python, делая крутые игры / Е. Свей-
гарт. – Пер. с англ. – М. : Ексмо, 2018.
13. Свейгарт Ел. Автоматизация рутинных задач с помощью
Python. Практическое руководство для начинающих / Е. Свей-
гарт. – Пер. с англ. – М. : ООО "И. Д. Вильямс", 2017.
14. Метиз Е. Изучаем Python. Программирование игр, визуали-
зация данных, веб-приложения / Е. Метиз. – СПб. : Питер, 2017.
15. Абрамов С. А. Начало программирования на языке Пас-
каль / С. А. Абрамов, Е. В. Зима. – М. : Наука. Гл. ред. физ.-мат.
лит., 1987.
361
ЗМІСТ
Передмова ......................................................................................... 3
Розділ 1. Вступ до програмування мовою Python ......................... 4
1.1. Особливості мови Python..................................................... 4
1.2. Дзен Python ........................................................................... 6
1.3. Основи синтаксису Python................................................... 7
1.3.1. Коментарі ...................................................................... 7
1.3.2. Літеральні константи ................................................... 8
1.3.3. Числа ............................................................................. 8
1.3.4. Рядки.............................................................................. 9
1.3.5. Змінні........................................................................... 11
1.3.6. Об'єкти......................................................................... 11
1.3.7. Логічні та фізичні рядки ............................................ 15
1.3.8. Відступи ...................................................................... 16
1.4. Оператори та вирази .......................................................... 19
1.4.1. Оператори ................................................................... 19
1.4.2. Порядок обчислення .................................................. 20
1.4.3. Зміна порядку обчислення......................................... 21
1.4.4. Асоціативність............................................................ 21
1.5. Керувальні структури ........................................................ 23
1.5.1. Логічні операції та операції порівняння .................. 23
1.5.2. Логічні оператори....................................................... 24
1.5.3. Виконання за умовою та порожнеча ........................ 25
1.5.4. Альтернативні гілки
програми (Chained conditionals) ............................... 26
1.5.5. Порожні блоки............................................................ 27
1.5.6. Вкладені умовні оператори
(Nested conditionals) ................................................... 28
1.6. Цикли. Оператор циклу while ........................................... 31
1.6.1. Лічильники.................................................................. 32
1.6.2. Нескінченні цикли...................................................... 33
1.6.3. Альтернативна гілка циклу while.............................. 34
1.6.4. Табулювання функцій................................................ 35
362
1.6.5. Вкладені оператори циклу
і двовимірні таблиці .................................................. 37
1.6.6. Анонімні функції (функція lambda) ......................... 38
1.6.7. Функція генератора.................................................... 39
1.7. Цикли. Оператор циклу for................................................ 41
1.7.1. Оператор break............................................................ 42
1.7.2. Оператор continue....................................................... 43
1.8. Визначення та використання
функцій і модулів ................................................................. 45
1.8.1. Функції ........................................................................ 45
1.8.2. Параметри функцій .................................................... 47
1.8.3. Локальні змінні........................................................... 48
1.8.4. Зарезервоване слово "global"..................................... 49
1.8.5. Зарезервоване слово "nonlocal"................................. 50
1.8.6. Значення аргументів за умовчанням ........................ 51
1.8.7. Ключові аргументи .................................................... 52
1.8.8. Змінна кількість параметрів ...................................... 53
1.8.9. Декоратори.................................................................. 54
1.8.10. Оператор return ......................................................... 58
1.8.11. Рядки документації .................................................. 59
1.8.12. Особливості використання
функцій у Python....................................................... 60
1.8.13. Рекурсивні функції................................................... 62
1.9. Використання модулів ....................................................... 64
1.9.1. Файли байткоду .pyc .................................................. 66
1.9.2. Оператор from ... import ... ........................................ 67
1.9.3. Ім'я модуля – __name__ ............................................. 67
1.9.4. Створення власних модулів ...................................... 68
1.9.5. Функція dir .................................................................. 69
1.9.6. Пакети ......................................................................... 71
1.10. Типи даних ........................................................................ 72
1.10.1. Цілочислові типи...................................................... 72
1.10.2. Логічні значення....................................................... 75
1.10.3. Тип чисел із плаваючою точкою............................. 75
1.10.4. Рядки.......................................................................... 78
1.10.5. Час і дата ................................................................... 85
363
1.11. Списки, кортежі та словники ............................................... 86
1.11.1. Список ....................................................................... 86
1.11.2. Кортеж....................................................................... 90
1.11.3. Словник ..................................................................... 92
1.11.4. Послідовності ........................................................... 94
1.11.5. Множина ................................................................... 96
1.11.6. Перетворення послідовностей ................................ 97
1.12. Робота з файлами............................................................ 105
1.12.1. Структуровані текстові файли. CSV..................... 106
1.12.2. Модуль pickle.......................................................... 108
1.13. Обробка виняткових ситуацій....................................... 110
1.13.1. Помилки .................................................................. 111
1.13.2. Виняток ................................................................... 111
1.13.3. Обробка винятків ................................................... 111
1.13.4. Виклик винятку ...................................................... 114
1.13.5. Try .. Finally............................................................. 115
1.13.6. Оператор with ......................................................... 116
1.14. Визначення та використання класів ............................. 117
1.14.1. Класи ....................................................................... 118
1.14.2. Методи об'єктів ...................................................... 118
1.14.3. Метод __init__ ........................................................ 119
1.14.4. Змінні класу та об'єкта........................................... 120
1.14.5. Спадкування ........................................................... 123
1.14.6. Метакласи ............................................................... 126
1.15. Проекти ігор.................................................................... 129
1.15.1. Проект № 1. Гра "Шибениця" ............................... 130
1.15.2. Проект № 2. Гра "Пінг-Понг"................................ 161
Розділ 2. Програмування задач мовою Python........................... 183
2.1. Програмування задач на числові послідовності............ 183
2.2. Рекурсія ............................................................................. 221
2.3. Непрямий виклик рекурсії............................................... 242
2.4. Хвостова та вкладена рекурсії ........................................ 243
2.5. Швидке піднесення до степеня
за допомогою рекурсії...................................................... 245
364
2.6. Ханойскі вежі.................................................................... 246
2.7. Нерекурсивний метод ...................................................... 249
2.8. Розширений синтаксис виклику функцій....................... 251
2.9. Структурні типи даних .................................................... 254
2.9.1. Масиви ...................................................................... 255
2.9.2. Використання масивів
як параметрів підпрограм ........................................ 269
2.9.3. Пошук і перестановка елементів масиву ............... 274
2.9.4. Методи сортування елементів масиву.................... 279
2.9.5. Деякі задачі з масивами чисел ................................ 328
2.9.6. Решето Ератосфена .................................................. 335
2.9.7. Схема Горнера для розрахунку
полінома n-го степеня .............................................. 346
2.9.8. Множини................................................................... 352
Література ..................................................................................... 361
365
Навчальне видання
Практичне
програмування
мовою Python
Підручник
Редактор Н. Земляна
Формат 60х841/16. Ум. друк. арк. 21,4. Наклад 100. Зам. № 219-9485.
Гарнітура Times New Roman. Папір офсетний. Друк офсетний. Вид. № Іт1.
Підписано до друку 20.11.19
Видавець і виготовлювач
ВПЦ "Київський університет"
01601, Київ, б-р Т. Шевченка, 14, кімн. 43
(044) 239 32 22; (044) 239 31 72; тел./факс (044) 239 31 28
e-mail: vpc_div.chief@univ.net.ua; redaktor@univ.net.ua
http: vpc.univ.kiev.ua
Свідоцтво суб'єкта видавничої справи ДК № 1103 від 31.10.02