Professional Documents
Culture Documents
Моделі - обчислень ДРУК
Моделі - обчислень ДРУК
«КИЄВО-МОГИЛЯНСЬКА АКАДЕМІЯ»
МОДЕЛІ ОБЧИСЛЕНЬ
У ПРОГРАМНІЙ ІНЖЕНЕРІЇ
Київ
Видавничий дім
«Києво-Могилянська академія»
2019
УДК 004.4/.9(075.8)
Г54
Рецензенти:
Анісімов А. В., член-кореспондент НАН України, доктор фізико-мате-
матичних наук, професор, декан факультету кібернетики Київ-
ського національного університету імені Тараса Шевченка;
Мейтус В. Ю., доктор фізико-математичних наук, старший науковий
співробітник Міжнародного науково-навчального центру інфор-
маційних технологій та систем НАН України та МОН України.
3
1.4.2. Блок-схеми та програми 38
1.4.3. Обчислення числової функції 39
1.4.4. Перетворення програм 41
1.4.5. Приклади деяких програм 42
1.4.6. Задачі 43
1.5. Системи Поста 44
1.5.1. Правило виведення і безпосереднє виведення 44
1.5.2. Означення системи Поста 46
1.5.3. Функція, обчислювана за Постом 47
1.5.4. Прості числові функції 48
1.5.5. Розширений алфавіт у системах Поста 49
1.5.6. Задачі 51
1.6. Частково-рекурсивні функції 52
1.6.1. Вступ. Базові функції 52
1.6.2. Операція суперпозиції. Функції-константи 53
1.6.3. Операція примітивної рекурсії 55
1.6.4. Прості арифметичні функції 56
1.6.5. Тотальні функції віднімання 57
1.6.6. Операція мінімізації 58
1.6.7. Частково визначені функції. Означення 59
1.6.8. Задачі 60
1.7. Лямбда-числення 61
1.7.1. Лямбда-вирази 61
1.7.2. Вільні та зв’язані змінні 62
1.7.3. Застосування 63
1.7.4. Цілі числа 65
4
1.7.5. Додавання і множення 66
1.7.6. Логічні значення і операції 67
1.7.7. Перевірка умови 67
1.7.8. Пари і попереднє число 68
1.7.9. Рівність і нерівність 69
1.7.10. Рекурсія 69
1.7.11. Задачі 71
1.8. Програма ModelComp 72
1.8.1. Встановлення програми 72
1.8.2. Призначення програми 72
1.8.3. Робота з машиною з необмеженими регістрами 75
1.8.4. Робота з машиною Тюрінґа 76
1.8.5. Робота з нормальним алгоритмом Маркова 78
1.8.6. Робота з системою Поста 80
1.8.7. Робота з частково-рекурсивними функціями 82
1.8.8. Робота з лямбда-виразами 84
1.8.9. Представлення моделей у текстовому файлі 86
2. АНАЛІЗ 92
2.1. Граматики 92
2.1.1. Означення: граматика, мова 92
2.1.2. Типи граматик 93
2.1.3. Означення дерева 94
2.1.4. Дерево виведення 95
2.1.5. Приклади КВ-граматик 96
2.1.6. Задачі 97
2.2. Скінченні автомати 98
5
2.2.1. Детермінований скінченний автомат 98
2.2.2. Недетермінований скінченний автомат 100
2.2.3. Побудова еквівалентного
детермінованого автомата 102
2.2.4. Регулярні граматики 103
2.2.5. Задачі 104
2.3. Регулярні вирази 105
2.3.1. Алгебра Кліні 105
2.3.2. Регулярні вирази 106
2.3.3. Регулярні вирази й скінченні автомати 107
2.3.4. Задачі 110
2.4. Контекстно-вільні граматики 111
2.4.1. Контекстно-вільна граматика 111
2.4.2. Лівостороння і правостороння вивідності 112
2.4.3. Дерево виведення 112
2.4.4. Мова, неоднозначність 113
2.4.5. Задачі 114
2.5. Властивості КВ-граматик 116
2.5.1. Приведення КВ-граматик 116
2.5.2. Перетворення КВ-граматик 118
2.5.3. Схеми граматик 121
2.5.4. Задачі 122
2.6. Магазинні автомати 124
2.6.1. Магазинний автомат і конфігурація 124
2.6.2. Приклад МП-автомата 126
2.6.3. Властивості МП-автоматів 127
6
2.6.4. Задачі 129
2.7. Синтаксичний аналіз 129
2.7.1. Задача синтаксичного аналізу 129
2.7.2. Приклад синтаксичного аналізу 130
2.7.3. Синтаксичні аналізатори 131
2.7.4. Синтаксичний аналіз згори вниз 132
2.7.5. Синтаксичний аналіз знизу вгору 133
2.8. LL(1)-граматики 134
2.8.1. Означення LL(1)-граматики 134
2.8.2. LL(1)-аналізатор 135
2.8.3. Приклад LL(1)-аналізатора 136
2.8.4. Побудова керівної таблиці 138
2.8.5. Побудова функцій first і follow 139
3. JAVA 141
3.1. Регулярні вирази 141
3.1.1. Регулярні вирази Java 141
3.1.2. Методи класу String 142
3.1.3. Класи Pattern та Matcher 143
3.1.4. Робота з групами 145
3.1.5. Розпізнавання дійсного числа 147
3.1.6. Задачі 148
3.2. Робота з КВ-граматиками 150
3.2.1. Клас Grammar 150
3.2.2. Вивідність 152
3.2.3. Синтаксичне дерево 153
3.2.4. Перетворення граматик 155
7
3.3. Робота з LL(1)-граматиками 156
3.3.1. LL(1)-граматики 156
3.3.2. LL(1)-аналіз 159
3.3.3. Побудова fst і nxt 159
3.3.4. Реалізація first і follow 162
3.4. Рекурсивний спуск 163
3.4.1. Метод рекурсивного спуску 163
3.4.2. Прості арифметичні вирази 167
3.4.3. Ітераційні форми та синтаксичні діаграми 170
3.4.4. Регулярні вирази 174
3.4.5. Задачі 176
3.5. Аналіз мов 177
3.5.1. Лексичний аналіз 177
3.5.2. Синтаксичний аналіз 181
3.5.3. Різні джерела символів 183
3.5.4. Абстрактні синтаксичні дерева 185
3.6. Лямбда-вирази 189
3.6.1. Потоки даних 189
3.6.2. Функціональні інтерфейси 192
3.6.3. Обробка масиву 194
3.6.4. Числа Фібоначчі 195
3.6.5. Текстові файли і слова 196
3.6.6. Бібліотека 199
3.6.7. Множина Мандельброта 202
3.6.8. Задачі 208
Список літератури 209
8
Моделі обчислень у програмній інженерії
1. Моделі
1.1. Вступ
Мета курсу – навести приклади, як впливає теорія на програ-
мування в програмній інженерії. Показати, які окремі теоретичні
напрацювання використовуються в мові програмування Java.
Існує декілька відомих моделей обчислень (Розділ 1), які
були створені в середині минулого століття і які утворили фун-
дамент сучасних мов програмування:
Нормальні алгоритми Маркова – найпростіша для ро-
зуміння модель обчислень;
Машини Тюрінґа й машини з необмеженими регістра-
ми – процедурне програмування;
Системи Поста – логічне програмування;
Частково-рекурсивні функції й лямбда-числення –
функціональне програмування.
Усі моделі обчислень породжують один і той самий клас
ефективних обчислюваних функцій (тезис Черча). З погляду
навчання, кожна модель обчислень – це проста мова програ-
мування, в основі якої лежить конкретна парадигма програ-
мування. А процес вивчення моделей обчислень можна звести до
побудови (програмування) функцій певного класу (традиційно-
числові функції натурального аргументу). Звичайно, для цього
необхідно мати програму зі зручним інтерфейсом (ModelComp),
яка дозволяє будувати, виконувати та зберігати різні моделі об-
числень. Програму ModelComp можна завантажити з репози-
торію https://github.com/ProtsenkoVS/models_interpreter.git.
Інший тип моделей – механізми породження формальних
мов (Розділ 2). Найбільш відомий із них – граматики Хомського.
На практиці найчастіше використовуються контекстно-залежні
(типу 1), контекстно-вільні (типу 2) та регулярні (типу 3) грама-
тики. Найпотужніші граматики (граматики типу 0) породжують
той самий клас мов, що й системи Поста.
9
Глибовець М. М., Кирієнко О. В., Проценко В. С.
10
Моделі обчислень у програмній інженерії
11
Глибовець М. М., Кирієнко О. В., Проценко В. С.
12
Моделі обчислень у програмній інженерії
13
Глибовець М. М., Кирієнко О. В., Проценко В. С.
14
Моделі обчислень у програмній інженерії
15
Глибовець М. М., Кирієнко О. В., Проценко В. С.
16
Моделі обчислень у програмній інженерії
17
Глибовець М. М., Кирієнко О. В., Проценко В. С.
**abb => *cab*b => b*cab* => **bbca => *cbb*ca =>
**bcbca => **cbcbca => *cc*bcbca => *db*cbca =>
b*dc*bca => b*db*ca => bb*dc*a => bb*da* => bba*d*
=> bba
o reverse(abb) = bba
18
Моделі обчислень у програмній інженерії
19
Глибовець М. М., Кирієнко О. В., Проценко В. С.
20
Моделі обчислень у програмній інженерії
14. –> d
У такому разі можна отримати алгоритм, що містить менше
підстановок і виконується швидше, якщо використати наступну
формулу:
1 + 1 + (2*1+1) + (2*2+1) + … + (2*(x-1)+1) =
= 2*(1+2+…+(x-1)) + x = x*(x-1) + x = x2.
Наступний алгоритм, що використовує додаткові символи a,
b, c, обчислює наведену формулу:
1. cb –> |c
2. c –>.
3. a| –> |bba
4. a –> b
5. b| –> |b
6. | –> a
7. –> c
Головну роботу виконують підстановки 3–6, які перетворю-
ють слово |x на слово bx*x. Інші підстановки 7 та 1–2 замінюють
усі символи b на символи |.
Приклад 9. Функція exponential3(x) = 3x обчислювана за
Марковим.
Нормальний алгоритм Маркова, який обчислює функцію
exponential3, використовує додаткові символи b, c, d і має
наступні підстановки:
1. b| –> |bbb
2. |b –> |c
3. |c –> c
4. c –> d
5. db –> |d
6. d –>.
7. b –>. |
8. –> b
Головну роботу виконують підстановки 8 (виконується один
x
раз) і 1, що слово |x перетворюють на слово |xb3 або,
використовуючи операцію ^ і дужки, – (|^x)(b^(3^x)). Підста-
21
Глибовець М. М., Кирієнко О. В., Проценко В. С.
1.2.7. Задачі
1. Побудувати нормальний алгоритм clearEndOne, який стирає
останній символ вхідного слова в алфавіті T = {a,b}.
2. Побудувати нормальний алгоритм addEndOne, який дописує
в кінець вхідного непорожнього слова x1x2…xn в алфавіті T =
{a,b} перший символ x1.
o невизначений для порожнього вхідного слова
3. Побудувати нормальний алгоритм transferIn10, який перево-
дить натуральні числа з одиничної системи числення (число
n кодується n-символами |) в десяткову систему.
4. Побудувати нормальний алгоритм copyWord, який кожне
слово x в алфавіті T = {a,b} переводить у слово xx.
5. Побудувати нормальний алгоритм, який обчислює функцію
expression1(x,y) = |x-(y+2)|
6. Побудувати нормальний алгоритм, який обчислює функцію
expression2(x,y,z) = x-(y+z)
o невизначена при x<y+z
7. Побудувати нормальний алгоритм, який обчислює функцію
expression3(x,y) = (x+2)*(y+3)
22
Моделі обчислень у програмній інженерії
23
Глибовець М. М., Кирієнко О. В., Проценко В. С.
24
Моделі обчислень у програмній інженерії
25
Глибовець М. М., Кирієнко О. В., Проценко В. С.
26
Моделі обчислень у програмній інженерії
27
Глибовець М. М., Кирієнко О. В., Проценко В. С.
28
Моделі обчислень у програмній інженерії
29
Глибовець М. М., Кирієнко О. В., Проценко В. С.
30
Моделі обчислень у програмній інженерії
31
Глибовець М. М., Кирієнко О. В., Проценко В. С.
32
Моделі обчислень у програмній інженерії
33
Глибовець М. М., Кирієнко О. В., Проценко В. С.
34
Моделі обчислень у програмній інженерії
1.3.9. Задачі
1. Побудувати машину Тюрінґа clearEndOne, яка стирає остан-
ній символ вхідного слова в алфавіті T = {a,b}.
2. Побудувати машину Тюрінґа addEndOne, яка дописує в
кінець вхідного непорожнього слова x1x2…xn в алфавіті T =
{a,b} перший символ x1.
результат невизначено для порожнього вхідного слова
3. Побудувати машину Тюрінґа transferIn10, яка переводить
натуральні числа з одиничної системи числення (число n ко-
дується n-символами |) в десяткову систему.
4. Побудувати машину Тюрінґа copyWord, яка кожне слово x в
алфавіті T = {a, b} переводить у слово xx.
5. Побудувати машину Тюрінґа, яка обчислює функцію
expression1(x,y) = |y-(x+2)|
6. Побудувати машину Тюрінґа, яка обчислює функцію
expression2(x,y,z) = z-(x+y)
o невизначена при z<x+y
7. Побудувати машину Тюрінґа, яка обчислює функцію
exponential2(x) = 2x
8. Побудувати машину Тюрінґа, яка обчислює функцію
expression4(x) = 3x+x
9. Побудувати машину Тюрінґа, яка обчислює функцію
35
Глибовець М. М., Кирієнко О. В., Проценко В. С.
divide3Remainder(x) = xmod3
o xmod3 – натуральне число – залишок від ділення
x на 3
10. Побудувати машину Тюрінґа, яка обчислює функцію
dividedQuatient(x,y) = xdivy
o xdivy – натуральне число – частка від ділення x
на y
o xdiv0 = 0
12. Побудувати машину Тюрінґа, яка обчислює функцію
gcd(x,y) = НСД(x,y)
НСД(x,y) – найбільший спільний дільник натуральних чи-
сел x та y
o НСД(x,0) = 0
o НСД(0,y) = 0
36
Моделі обчислень у програмній інженерії
37
Глибовець М. М., Кирієнко О. В., Проценко В. С.
38
Моделі обчислень у програмній інженерії
39
Глибовець М. М., Кирієнко О. В., Проценко В. С.
№ Команда Коментар
1 Z(3) r3 := 0
2 J(3,2,6) ifr3=r2 then goto 6
3 S(1) r1 := r1+1
4 S(3) r3 := r3+1
5 J(1,1,2) goto2 – безумовний перехід
Блок-схему програми наведено на мал. 2, а саму програму з
коментарями до кожної команди – в таблиці 2.
Послідовність команд 2, 3, 4 і 5, що може виконуватися ба-
гато разів, називають циклом:
команда 2 – перевірка умови циклу;
команди 3–5 – тіло циклу.
Під час виконання цієї програми для довільної початкової
конфігурації машина завжди зупиниться:
Якщо y=0, то умова ко-
манди 2 виконається від-
разу й машина спробує зро-
бити перехід на команду 6,
що призведе до зупинки.
Якщо y>0, то умова коман-
ди 2 виконається після y
кроків тіла циклу.
Приклад 3. Частково-визначена
функція substraction(x,y) = x-y, ви-
значена лише для аргументів x≥y,
обчислювана.
Блок-схема потрібної МР-про-
грами має структуру, схожу на по-
40
Моделі обчислень у програмній інженерії
41
Глибовець М. М., Кирієнко О. В., Проценко В. С.
Ck+1 = T(1,1).
Ct = It, t<k+1, якщо команда It – Z, S, T.
Ct = J(m,n, q1), t<k+1, якщо It = J(m,n, q) і q1 = min(q,k+1).
Нескладно з’єднати дві стандарні програми P = <I1, I2, …, Ik>
і Q = <V1, V2, …, Vt> так, щоб нова програма W спочатку обчис-
лювалася як програма P, а потім продовжила обчислення як про-
грама V, початковою конфігурацією якої є заключна конфігура-
ція програми програма P. Така програма фактично реалізує по-
слідовне виконання програм P і V.
Програма W = <I1, I2, …, Ik, V11, V12, …, V1t>, де:
V1i = Vi, якщо команда Vi є Z, S, T.
V1i = J(m,n,q+k), якщо Vi = J(m,n, q).
Використовуючи з’єднання програм і програму, що об-
числює addition, можна легко побудувати програми, що об-
числюють наступні функції:
addition3(x,y,z) = x+y+z
mult3(x) = 3*x
На мал. 4 і 5 наведено блок-схеми цих програм, на кожній із
них програми, що обчислюють addition, позначаються прямо-
кутником із подвійними бічними сторонами.
Можна виконати складнішу операцію: вставити стандартну
програму Q = <V1, V2, …, Vt> у середину програми P = <I1, I2, …,
Ik> після команди з номером m<k і отримати нову програму W =
<I11, I12, …, I1m, V11, V12, …, V1t, I1m+1, …, I1k>, у якої:
I1i = Ii, якщо команда Ii є Z, S, T.
I1i = J(m1,n1,q), якщо Ii = J(m1,n1, q) і q≤m.
I1i = J(m1,n1,q+t), якщо Ii = J(m1,n1, q) і q>m.
V1i = Vi, якщо команда Vi є Z, S, T.
V1i = J(m1,n1,q+m), якщо Vi = J(m1,n1, q).
42
Моделі обчислень у програмній інженерії
1.4.6. Задачі
Побудувати МР-програму, яка обчислює функцію:
1. signum(x) = sg(x)
для натуральних чисел sg(x) = 0 при x=0 і sg(x) =
= 1 при x>0
2. expresion1(x,y,z) = z-(x+y)
43
Глибовець М. М., Кирієнко О. В., Проценко В. С.
44
Моделі обчислень у програмній інженерії
45
Глибовець М. М., Кирієнко О. В., Проценко В. С.
46
Моделі обчислень у програмній інженерії
47
Глибовець М. М., Кирієнко О. В., Проценко В. С.
48
Моделі обчислень у програмній інженерії
49
Глибовець М. М., Кирієнко О. В., Проценко В. С.
50
Моделі обчислень у програмній інженерії
1.5.6. Задачі
Довести, що наступні функції обчислювані за Постом:
1. notSignum(x) = nsg(x)
51
Глибовець М. М., Кирієнко О. В., Проценко В. С.
52
Моделі обчислень у програмній інженерії
53
Глибовець М. М., Кирієнко О. В., Проценко В. С.
54
Моделі обчислень у програмній інженерії
55
Глибовець М. М., Кирієнко О. В., Проценко В. С.
56
Моделі обчислень у програмній інженерії
57
Глибовець М. М., Кирієнко О. В., Проценко В. С.
58
Моделі обчислень у програмній інженерії
59
Глибовець М. М., Кирієнко О. В., Проценко В. С.
1.6.8. Задачі
Довести, що наступні функції примітивно-рекурсивні.
1. signum(x) = sg(x)
для натуральних чисел sg(x) = 0 при x=0 і sg(x) = 1
при x>0
2. expression1(x,y,z) = |x–(y+z)|
3. expresion2(x,y) = (x +1)* y
4. less(x,y)
набуває значення 1 при x<y та 0 при x≥y
5. lessEqual(x,y)
набуває значення 1 при x≤y та 0 при x>y
6. maximum(x,y) = max(x, y)
7. factorial(x) = x! = 1*2* …*(x–1)* x
8. expression3(x,y) = 3x + y
9. expression4(x) = (x+1)div3
(x+1)div3 – натуральне число – частка від ділення
(x+1) на 3
10. dividedQuotient(x,y) = xdivy
xdivy – натуральне число – частка від ділення x
на y
11. gcd(x,y) = НСД(x,y)
60
Моделі обчислень у програмній інженерії
1.7. Лямбда-числення
1.7.1. Лямбда-вирази
Лямбда-числення (-числення) можна назвати найменшою
універсальною мовою програмування у світі. Основне поняття в
лямбда-численні – вираз. Лямбда-числення містить лише одне
правило перетворень (підстановка змінної) й лише одну схему
визначення функції.
Поняття «лямбда-числення» було введене в науковий обіг у
1930-х роках Алонзо Черчем як один із способів формалізації
поняття ефективного обчислення. Лямда-числення універсальне
тому, що в його межах можна побудувати й обрахувати довільну
обчислювану функцію. Воно еквівалентне машинам Тюрінґа.
Але лямбда-числення використовує правила перетворення
(підстановки), не турбуючись про те, як їх реалізує комп’ютер у
реальності. Цей підхід більше пов’язаний із програмним забез-
печенням (software), ніж із організацією комп’ютера (hardware).
Означення 1. Вираз expr – це:
Змінна – довільний ідентифікатор: id
Функція – λ id . expr
Застосування – expr1 expr2
Використовуються лише два ключові слова (символи): λ і.
Для наочності вираз можна взяти в круглі дужки. Тобто якщо
expr – вираз, то (expr) – той самий вираз.
Щоб не вживати багато дужок, приймається угода, що опе-
рація застосування – асоціативна ліворуч. Тобто вираз expr1 expr2
expr3 … exprN вираховується так само, як наступний вираз:
(…((expr1 expr2) expr3) … exprN).
Приклад 1. Згідно з означенням лямбда-виразу, окремий
ідентифікатор – лямбда-вираз.
61
Глибовець М. М., Кирієнко О. В., Проценко В. С.
Найпростішою функцією є
λx.x
Цей вираз визначає тотожну функцію (identity function). Ім’я
(ідентифікатор) після λ – ідентифікатор аргументу цієї функції.
Вираз після крапки (в цьому разі лише x) називають «тілом»
визначення.
Означення 2. Функції можна застосовувати до виразів. На-
приклад:
(λ x . x) y
Тотожна функція застосовується до y. Дужки використову-
ють для наочності, щоб позбутися неоднозначності.
Застосування функції обчислюється підстановкою замість
аргументу x значення (у нашому прикладі – y) в тіло визначення
функції, тобто
(λ x . x) y = [y / x] x = y
У цьому перетворенні позначення [y / x] використовують,
щоб відзначити, що всі входження аргументу x замінюються на
значення y у виразі праворуч – x. Підстановку часто називають
редукцією.
Імена аргументів у визначеннях функцій не мають ніякого
змістовного навантаження. Це просто позначення розміщення,
тобто вони використовуються, щоб показати, де розташовані
аргументи функції, коли вона обчислюється. Тому
(λ z . z) ≡ (λ y .y) ≡ (λ t .t) ≡ (λ u . u)
тощо. Символ ≡ уживається, аби показати, що коли A ≡ B, то A –
просто синонім B.
62
Моделі обчислень у програмній інженерії
(λ x . x) (λ y . y x)
х у тілі першого виразу, що ліворуч, – зв’язана першим λ. У тілі
другого виразу y – зв’язана другим λ, а x – вільна. Зауважимо,
що x у другому виразі повністю незалежна від x у першому
виразі.
Означення 3. Змінна x вільна у виразі, якщо маємо один із
наступних трьох випадків:
x – вільна у виразі x
x – вільна в λ x1.expr, якщо ідентифікатор x1 ≠ x і x –
вільна в expr
x – вільна в expr1 expr2, якщо x – вільна в expr1, або вона
вільна в expr2
Означення 4. Змінна x зв’язана, якщо маємо один із наступ-
них двох випадків:
x – зв’язана в λ x1.expr, якщо ідентифікатор x1= x або x –
зв’язана в expr
x – зв’язана в expr1 expr2, якщо x – зв’язана в expr1, або
вона зв’язана в expr2
Варто підкреслити, що один і той самий ідентифікатор може
бути водночас вільним і зв’язаним в одному й тому самому ви-
разі. У виразі
(λ x . x y) (λ y . y)
перша змінна y – вільна у лівому підвиразі в дужках. Але друга
змінна y – зв’язана в другому підвиразі праворуч. Отже, іденти-
фікатор y у цілому виразі водночас вільний і зв’язаний.
1.7.3. Застосування
Найбільше здивування під час знайомства з лямбда-числен-
ням викликає той факт, що немає потреби давати імена
функціям. Кожний раз, коли необхідно застосувати функцію,
записується все означення функції й лише потім виконується її
обчислення. Щоб спростити позначення, ми використовувати-
мемо ідентифікатори й числа як синоніми для деяких означень
63
Глибовець М. М., Кирієнко О. В., Проценко В. С.
64
Моделі обчислень у програмній інженерії
65
Глибовець М. М., Кирієнко О. В., Проценко В. С.
66
Моделі обчислень у програмній інженерії
Добуток 2 на 2 – це
(λ x y z . x (y z)) 2 2
що зводиться до
(λ z . 2 (2 z))
Можна перевірити, що в разі подальших застосувань (ре-
дукцій) отримаємо очікуваний результат 4.
67
Глибовець М. М., Кирієнко О. В., Проценко В. С.
68
Моделі обчислень у програмній інженерії
1.7.10. Рекурсія
Рекурсивні функції можна визначити в лямбда-численні, ви-
користовуючи функції, які, викликавши іншу функцію f, потім
регенерують самі себе. Це краще зрозуміти, якщо розглянути
одну з таких функцій fix:
69
Глибовець М. М., Кирієнко О. В., Проценко В. С.
70
Моделі обчислень у програмній інженерії
1.7.11. Задачі
Побудувати лямбда-вирази, що обчислюють наступні
функції:
1. expression1(x,y,z) = x+y+z
2. signum (x) = sg(x)
для натуральних чисел sg(x) = 0 при x=0 і sg(x) =
= 1 при x>0
3. expresion2(x,y) = (x +1)* y
4. less(x,y)
приймає значення True при x<y та False при x≥y
функція повертає значення 0 (False) або True
5. lessEqual(x,y)
приймає значення True при x≤y та False при x>y
функція повертає значення 0 (False) або True
6. maximum(x,y) = max(x, y)
7. factorial(x) = x! = 1*2* …*(x–1)* x
8. expression3(x,y) = 3x + y
9. divide3Quatient(x) = xdiv3
xdiv3 – натуральне число – частка від ділення
x на 3
10. dividedQuotient(x,y) = xdivy
xdivy – натуральне число – частка від ділення
x на y
11. gcd(x,y) = НСД(x,y)
НСД(x,y) – найбільший спільний дільник нату-
ральних чисел x і y
НСД(x,0) = 0
НСД(0,y) = 0
71
Глибовець М. М., Кирієнко О. В., Проценко В. С.
72
Моделі обчислень у програмній інженерії
73
Глибовець М. М., Кирієнко О. В., Проценко В. С.
74
Моделі обчислень у програмній інженерії
75
Глибовець М. М., Кирієнко О. В., Проценко В. С.
76
Моделі обчислень у програмній інженерії
77
Глибовець М. М., Кирієнко О. В., Проценко В. С.
78
Моделі обчислень у програмній інженерії
79
Глибовець М. М., Кирієнко О. В., Проценко В. С.
80
Моделі обчислень у програмній інженерії
81
Глибовець М. М., Кирієнко О. В., Проценко В. С.
82
Моделі обчислень у програмній інженерії
83
Глибовець М. М., Кирієнко О. В., Проценко В. С.
84
Моделі обчислень у програмній інженерії
85
Глибовець М. М., Кирієнко О. В., Проценко В. С.
86
Моделі обчислень у програмній інженерії
87
Глибовець М. М., Кирієнко О. В., Проценко В. С.
o i: S(n) [Comm]
o i: T(n,m) [Comm]
o i: J(n,m,q) [Comm]
o i, n, m, q – числа, що задають номер команди
(i) і її аргументи – (n,m,q).
Машина Тюрінґа
[Comm]
Machine Id
Alphabet Al1, Al2; [Numerical n;]
Initial ini; Final fin;
MoveState1
……
MoveStatek
end Id
Comm – загальний коментар моделі.
Id – ім’я моделі.
Al1, Al2 – рядки, які задають, відповідно, основний і
додатковий алфавіти.
ini, fin – рядки, які задають, відповідно, початковий і
заключний стан машини.
o Стан машини Тюрінґа – це рядок “@XY”, X,
Y – довільні символи.
MoveState1, …, MoveStatek – список правил машини,
які задають таблицю переходів. Кожне з правил опи-
сує поведінку машини в одному зі станів, розташо-
вується в окремому рядку й має вигляд:
o State: St1 –>Move1: : Stn –> Moven; [Comm]
o State – рядок, що задає стан машини.
o St1, …, Stn – рядки, кожний із яких є симво-
лом алфавіту (основного або додаткового).
o Move1, …, Moven – рядки “@XYSM”, що
описують поведінку машини у стані State із
символом St1, …, Stn:
@XY – наступний стан машини;
88
Моделі обчислень у програмній інженерії
Система Поста
[Comm]
System Id
Alphabet Al1, Al2; [Numerical n;]
RuleSyst1
……
RuleSystk
end Id
Comm – загальний коментар моделі.
Id – ім’я моделі.
Al1, Al2 – рядки, які задають, відповідно, основний і
додатковий алфавіти.
RuleSyst1, …, RuleSystk – список аксіом і правил виве-
дення системи, кожний із яких розташований в окре-
мому рядку й має вигляд:
o St1 ; [Comm]
аксіома
o St1 –> St2; [Comm]
правило виведення
o St1, St2 – рядки, у яких можуть вживатися
символи основного й додаткового алфавіту та
змінні. Кожна змінна – це пара символів @R,
@S, @T, @U, @V, @W, @X, @Y або @Z.
89
Глибовець М. М., Кирієнко О. В., Проценко В. С.
90
Моделі обчислень у програмній інженерії
Набір лямбда-виразів
[Comm]
CalculusLid;
Lambda1
……
Lambdak
end Lid
Модель з іменем Lid і можливим коментарем Comm
складається з послідовності лямбда-виразів з іменами
Lambda1, …, Lambdak.
Кожний вираз описується або двома рядками
o ‘ Comment lambda
o Idl = \ x1 x2 … xk . Expr
або одним рядком (вираз без коментаря)
o Idl = \ x1 x2 … xk . Expr
Кожний вираз описується в одному рядку й має
вигляд:
o Idl = \ x1 x2 … xk . Expr
o Idl – ім’я лямбда-виразу.
o x1, x2, …, xk – ідентифікатори, що задають
змінні лямбда-виразу.
o Expr – вираз, який має вигляд:
xi – змінна;
idP – ідентифікатор раніше визначе-
ного лямбда-виразу;
n – десятковий запис цілого числа без
знака;
(e) – лямбда-вираз e в дужках;
efea – операція застосування – два
лямбда-вирази ef та ea;
91
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2. Аналіз
2.1. Граматики
2.1.1. Означення: граматика, мова
Алфавіт V – це довільна скінченна множина. Елементи цієї
множини – це символи алфавіту. Слово α – довільна скінченна
послідовність символів V. Через V* позначаються всі слова в
алфавіті V. Якщо α – слово, то |α| – довжина слова α. Порожнє
слово (порожня послідовність символів) позначається ε та |ε| = 0.
Означення 1. Мова L в алфавіті V – це довільна підмножина
множини V*. Тобто L V*.
Означення 2. Граматика G = (VN, VT, P, S), де:
VN і VT – алфавіти, відповідно, нетерміналів і терміналів,
VN VT = {}.
V = VN VT.
P – скінченна множина правил α–>β, де α = α1Nα2, α1 V*,
N VN, α2 V* і β V*.
S – початковий нетермінал, S VN.
Означення 3. Якщо α–>β Pіγ, δ V*, то в граматиці G зі
слова γαδ безпосередньо виводиться слово γβδ, що позначається
γαδ=>γβδ.
u=>v, якщо існують γ, δ V* і α–>β P такі, що u=γαδ і
v=γβδ.
u=>v – відношення безпосереднього виведення на мно-
жині V*.
Означення 4. Якщо α1, α2, …, αn V*, α1=>α2, …, αn-1=> αn, то
в граматиці G зі слова α1 виводиться слово αn, що позначається
α1*=>αn.
Послідовність α1=> α2=> … => αn називається виведен-
ням слова αn зі слова α1.
u*=>v – відношення виведення на множині V*.
92
Моделі обчислень у програмній інженерії
93
Глибовець М. М., Кирієнко О. В., Проценко В. С.
L(G2) = {anbn | n ≥ 0}
Виведення слова aabb має вигляд:
o S =>aSb =>aaSbb =>aabb
Приклад 3. У граматиці G3 = (VN, VT, P, S) VN = {S,A}, VT =
= {a,b}, P = {S –>aS, S –>aA, A –>bA, A –>ε}
G3 – граматика типу 3 (регулярна)
L(G3) = {anbm | n>0, m ≥ 0}
Виведення слова aaab має вигляд:
o S =>aS =>aaS =>aaaA => aaabA => aaab
Властивість 3. Якщо AL0, AL1, AL2, AL3 – усі мови типу 0,
1, 2 і 3 відповідно, то маємо співвідношення
AL3 AL2 AL1 AL0
Властивість 4. Системи Поста породжують усі мови типу 0 і
тільки їх.
94
Моделі обчислень у програмній інженерії
95
Глибовець М. М., Кирієнко О. В., Проценко В. С.
96
Моделі обчислень у програмній інженерії
2.1.6. Задачі
1. Чи можна вивести слово cabbaac у граматиці: G = (VN,
VT, P, S), VN = {S, A, B}, VT = {a, b, c}, P = {S –>SS, S –>a,
S –>c, S –>A, B –>bB, B –>b, aAa –>aBa}? Побудувати
виведення цього слова.
2. Чи належить рядок (()())() мові, що породжується КВ-
граматикою G = (VN, VT, P, S), VN = {S, A}, VT= {(, )}, P =
= {S –>SA, S –> A, A –>(S), A –> ( ) }? Побудувати виве-
дення цього слова.
3. Чи належить слово 00011011 мові, що породжується КВ-
граматикою G = (VN, VT, P, S), VN = {S, A}, VT = {0, 1}, P =
= {S –>SS, S –> A, A –>0A1, A –> S, A –> 01}? Побудуйте
виведення цього слова.
4. До якого типу належить граматика G = (VN, VT, P, S), VN =
= {S, A, B}, VT = {a, b, c, s}, P = {S –>AcBs, A –>AcA, A –>
B, B –>a, B –>b}? Яку мову вона породжує? Чи можна
породити цю мову граматикою більшого типу?
5. До якого типу належить граматика G = (VN, VT, P, S), VN =
= {S, A, B}, VT = {a, b, c}? Яку мову вона породжує?
a. P = { S –>A, S –>B, A –>aAb, A –>a, B –>aBbb,
B –>b}
b. P = { S –>aA, S –>bS, A –>aA, A –>bB, B –>aB,
B –>bB, B –>a}
97
Глибовець М. М., Кирієнко О. В., Проценко В. С.
98
Моделі обчислень у програмній інженерії
99
Глибовець М. М., Кирієнко О. В., Проценко В. С.
100
Моделі обчислень у програмній інженерії
101
Глибовець М. М., Кирієнко О. В., Проценко В. С.
102
Моделі обчислень у програмній інженерії
103
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2.2.5. Задачі
1. Побудувати детермінований скінченний автомат, що
розпізнає мову, задану регулярним виразом a*(a+b)*a.
2. Побудувати недетермінований скінченний автомат, що
розпізнає усі слова в алфавіті {a,b,c}, за винятком слів
мови, заданої регулярним виразом c*(a+b)*c.
3. Побудувати скінченний недетермінований автомат, що
розпізнає мову в алфавіті T = {a,b}, яка складається зі
слів:
a. у яких друга літера від кінця – b;
b. що починаються й закінчуються різними літе-
рами;
c. що містять не менше, ніж два входження сим-
волу a.
4. Побудувати скінченний детермінований і недетермінова-
ний автомати, що розпізнають мову в алфавіті T = {a,b},
яка складається зі слів:
a. що закінчуються ланцюжком ааbba;
b. у яких за кожним символом а безпосередньо
йде b.
5. Побудувати скінченний детермінований автомат, який
допускає над алфавітом T = {a,b}:
a. порожню мову;
b. мову, що містить лише одне слово – порожній
ланцюжок;
c. усі слова, що включають ланцюжок аba;
104
Моделі обчислень у програмній інженерії
105
Глибовець М. М., Кирієнко О. В., Проценко В. С.
106
Моделі обчислень у програмній інженерії
107
Глибовець М. М., Кирієнко О. В., Проценко В. С.
108
Моделі обчислень у програмній інженерії
R
0
якщо {ai1, ai2, …, aik} = {a | σ(pi, a) = pj}, то ij = ai1 | ai2 |
… | aik
Перехід (s,w) *=> (t,u) із конфігурації (s,w) в конфігурацію
(t,u) називається переходом через стани p1, …, pk і позначається
(s,w) *=1...k => (t,u) тоді й тільки тоді, коли існують конфігу-
рації (q1,x1) = (s,w), (q2,x2), …, (qn,xn) = (t,u) такi, що (qi,xi) =>
(qi+1,xi+1), i=1,…,n–1; q2,…, qn-1{p1,…, pk}; q1 = s; qn = t. У про-
цесі такого переходу в якості проміжних вершин використову-
ються лише вершини з {p1,…, pk}.
Покажемо, як побудувати регулярний вираз Rij , що задає
k
мову (множину)
{w | (pi,w) *= 1…k => (pj,ε)}
k 1 *
Rij = Rij | Rik ( Rkk ) Rkj
k k 1 k 1 k 1
R
k
Вираз ij будується (задається) із використанням виразів
R
k 1
tq .
R
n
Регулярний вираз ij задає мову (множину) {w | (qi,w) *=>
(qj,ε)}.
Якщо F = {pi1, …, pim}, то вираз
Rq 0 pi1 | Rqopi2 | … | R
n n n
q0 pim
109
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2.3.4. Задачі
1. Побудувати регулярний вираз, що задає мову L в алфавіті
T = {a,b}, яка складається зі слів, у яких друга літера від
кінця – b.
2. Побудувати регулярний вираз, що задає мову L в алфавіті
T = {a,b}, яка складається зі слів, що починаються й
закінчуються різними літерами.
3. Задайте мову над алфавітом {0, 1}, усі слова якої почина-
ються з символу 0 і містять, як мінімум, два символи 1, за
допомогою:
a. регулярної граматики;
b. регулярного виразу;
c. скінченного недетермінованого автомата;
d. скінченного детермінованого автомата.
4. Задайте мову над алфавітом {0, 1}, усі слова якої не
містять двох одиниць поспіль, за допомогою:
a. регулярної граматики;
b. регулярного виразу;
c. скінченного недетермінованого автомата;
d. скінченного детермінованого автомата.
5. Задайте всі слова десяткових цифр, які представляють
число, що ділиться на 5:
a. регулярною граматикою;
b. регулярним виразом;
c. скінченним недетермінованим автоматом;
d. скінченним детермінованим автоматом.
6. Мова L в алфавіті T = {a,b} складається зі слів, що мі-
стять точно один символ b. Побудувати:
a. скінченний недетермінований автомат, що роз-
пізнає мову L;
b. граматику типу 3, що породжує мову L;
c. регулярний вираз, що задає мову L;
d. скінченний детермінований автомат, що розпізнає
мову L.
110
Моделі обчислень у програмній інженерії
111
Глибовець М. М., Кирієнко О. В., Проценко В. С.
112
Моделі обчислень у програмній інженерії
113
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2.4.5. Задачі
1. Задано граматику G = (VN, VT, P, S), VN = {S, D, L}, VT =
{0, 1, a, b}, P = {S –>LDL, L –>La, L –>Lb, L –> a, D –>
D0, D –> D1, D –> 0, D –> 1}.
a. Побудувати дерева виведення наступних слів:
ab10aa, abba0a, a1a.
114
Моделі обчислень у програмній інженерії
115
Глибовець М. М., Кирієнко О. В., Проценко В. С.
116
Моделі обчислень у програмній інженерії
117
Глибовець М. М., Кирієнко О. В., Проценко В. С.
118
Моделі обчислень у програмній інженерії
Слово в ал-
фавіті d-d-d L(G)
має два різні
лівосторонні
виведення.
S => S-S
=> d-S =>
d-S-S => d-d-S => d-d-d
S => S-S => S-S-S => d-S-S
=> d-d-S => d-d-d
Цим виведенням відповідають
два різні дерева виведення (мал. 1 і
мал. 2 відповідно).
Неоднозначність граматики відоб-
ражає два можливі варіанти обчис-
лення виразу d-d-d.
119
Глибовець М. М., Кирієнко О. В., Проценко В. С.
120
Моделі обчислень у програмній інженерії
121
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2.5.4. Задачі
1. За контекстно-вільною граматикою
G = (VN, VT, P, S), VN = {S, A, B, C, E}, VT = {0, 1, a}, P = {S
–>0A0, S –>1B1, S –>BB, A –>C, A –>EaA, B –>S, B –>A,
C –>S, C –>ε, C –>CE, E –>CE, E –>aE}
побудувати еквівалентну їй контекстно-вільну:
d. приведену граматику;
e. граматику без ланцюгових правил виведення;
f. граматику без анулювальних правил виведення
A –>ε.
2. За контекстно-вільною граматикою
G = (VN, VT, P, S), VN = {S, B}, VT = {a, b}, P = {S –>aSbS,
S –>BaB, B –>ε, B –>bS}
побудувати еквівалентну їй контекстно-вільну граматику
без анулювальних правил виведення A –>ε. Яку мову
вона породжує?
122
Моделі обчислень у програмній інженерії
3. За контекстно-вільною граматикою
G = (VN, VT, P, S), VN = {S, A, B, C, D, E}, VT = {a, b, d},
P = {S –>aA,
S –>aBB, A –>aaA, A –>ε, B –>bB, B –>bbC, C –>B,
C –>DdA, E –>CE}
побудувати еквівалентну їй контекстно-вільну:
a. приведену граматику;
b. граматику без ланцюгових правил виведення;
c. граматику без анулювальних правил виведення
A –>ε.
4. За контекстно-вільною граматикою
G = (VN, VT, P, S), VN = {S, A, B, C, E}, VT = {a, b, c, d},
P = {S –>ASB,
S –>aB, A –>aAS, A –>a, A –>aEb, B –>SbS, B –>A,
B –>bb, B –>ε, C –>cC, C –>EdA, E –>cE, E –>BdC}
побудувати еквівалентну їй контекстно-вільну:
d. приведену граматику;
e. граматику без ланцюгових правил виведення;
f. граматику без анулювальних правил виведення
A –>ε.
5. Побудуйте усі дерева виведення слова a;a;a;a у грама-
тиці G = ({S}, {a,;}, {S –>S;S, S –>a}, S), що породжує
послідовності символів a, розділених крапкою з комою.
Ця граматика – неоднозначна. Неоднозначність дерева
виведення в цій граматиці пояснюється можливістю по-
різному структурувати групу символів, що стоять поряд
(правило S –>S;S).
a. Побудуйте дві однозначні граматики, які ви-
окремлють по одному символу з такої групи або
ліворуч, або праворуч.
b. Побудуйте дерево виведення слова a;a;a;a в
побудованих граматиках.
123
Глибовець М. М., Кирієнко О. В., Проценко В. С.
124
Моделі обчислень у програмній інженерії
x – вхідне слово.
Означення 4. Заключна конфігурація (q, ε, ε), q ∈ Q –
довільний.
Означення 5. МП-автомат може переходити з однієї конфігу-
рації в іншу згідно з функцією переходів σ, породжуючи на мно-
жині конфігурації відношення переходу ╞>.
Якщо поточна конфігурація (q, ax, Zw) і σ(q, a, Z) = {(p1,
γ1), …, {(pm, γm)}, q ∈ Q, a T, Z U, для 1≤i≤m, pi ∈ Q, γi
U*, то МП-автомат може виконати один із m-переходів
1≤i≤m – (q, ax, Zw)╞> (pi, x, γiw).
o МП-автомат читає символ a на вході, заміняє
символ Z на вершині магазину на слово γi й
переходить зі стану q у стан pi.
Якщо поточна конфігурація (q, x, Zw) і σ(q, ε, Z) = {(p1,
γ1), …, {(pm, γm)}, q∈Q, a
T, Z U, для 1≤i≤m,
pi ∈ Q, γi U*, то МП- (q0,abba,Z0)
зину на слово γi й
переходить зі
стану q у стан pi, (q0,a,BBAZ0) (q1,a,AZ0)
нічого не чита-
ючи на вході.
Означення 6. Розширення
(q0,ε,ABBAZ0)
переходу ╞> відношення ╞*> – (q1,ε,Z0)
це рефлексивне й транзитивне
замикання відношення ╞>.
Тобто (q0, x0, α0) ╞*> (qn, xn, αn) (q1,ε,ε)
125
Глибовець М. М., Кирієнко О. В., Проценко В. С.
126
Моделі обчислень у програмній інженерії
127
Глибовець М. М., Кирієнко О. В., Проценко В. С.
(q,abba,S)
(q,bba,Sa)
(q,ba,Sba)
(q,a,Sbba) (q,a,a)
(q,a,aSabba)
магазин не (q,a,bSbba) (q,a,bba) (q,ε,ε)
очиститься
Мал. 3
Дерево можливих конфігурацій, яке описує поведінку цього
а на вхідному слові abba, наведено на мал. 3.
Теорема 2. Якщо M – недетермінований МП-автомат, то
L(M) – КВ-мова.
Доведення. Нехай M = (Q, T, U, σ, q0, Z0) – МП-автомат.
Розглянемо КВ-граматику G = (VN, VT, P, S), у якої VT = T, VN =
128
Моделі обчислень у програмній інженерії
2.6.4. Задачі
1. Побудувати МП-автомат, що розпізнає такі слова в алфа-
віті {a, b}, у яких кількість входжень букви a не переви-
щує кількості входжень букви b.
2. Побудувати МП-автомат, що розпізнає мову правильних
виразів, складених із дужок. Наприклад, слово (( )(( )) )( )
належить цій мові, а слово )( ))) – ні.
a. Побудувати КВ-граматику, що породжує мову
правильних виразів, складених із дужок.
3. Побудувати МП-автомат, що розпізнає слова мови
{anb2n | n ≥ 0}.
129
Глибовець М. М., Кирієнко О. В., Проценко В. С.
130
Моделі обчислень у програмній інженерії
131
Глибовець М. М., Кирієнко О. В., Проценко В. С.
132
Моделі обчислень у програмній інженерії
133
Глибовець М. М., Кирієнко О. В., Проценко В. С.
2.8. LL(1)-граматики
2.8.1. Означення LL(1)-граматики
Означення 1. Нехай G = (VN, VT, P, S) – КВ-граматика з
об’єднаним алфавітом V = VN VT.
Якщо α V*, то first(α) = {a VT | α*=lm>ax, x VT*} {ε |
α*=>ε}
o Очевидно, що first(a) = {a}, a VT, first(ε) = {ε}
G – LL(1)-граматика, якщо для довільних двох ліво-
сторонніх виведень:
o S *=lm> wAα =lm> wβα *=lm> wx, A –> β P
o S *=lm> wAα =lm> wγα *=lm> wy, A –> γ P
у яких first(x) = first(y), маємо β = γ.
Означення 2. КВ-граматика G – проста LL(1)-граматика,
якщо в ній немає анулювальних правил виведення (A –>ε) і ВСІ
альтернативи для кожного нетермінала починаються з різних
терміналів.
Приклад 1. КВ-граматика G = ({S, B}, {a, b}, P, S) із множи-
ною правил виведення P = {S –>aBS, S –>b, B –>a, B –>bSB} –
проста LL(1)-граматика.
Властивість 1. КВ-граматика (VN, VT, P, S) – LL(1)-граматика
тоді й тільки тоді, коли
first(βα) first(γα) = {}
для всіх α, β, γ таких, що існують A –>β P, A –>γ P, β ≠ γ і S
*=lm>wAα.
134
Моделі обчислень у програмній інженерії
2.8.2. LL(1)-аналізатор
Означення 4. LL(1)-аналізатор для LL(1)-граматики M = (VT,
V {$}, W, σ, S, $), де:
VT – вхідний алфавіт;
V {$} – алфавіт магазину;
$ V – «дно» магазину;
W = {1, 2, …, n} – вихідний алфавіт – список номерів
правил виведення граматики;
S – початковий символ магазину (початковий нетермінал
граматики);
σ – керівна таблиця, σ: VN×(VT {$})−>W {E}, де дії :
o i W – Заміна (change) верхнього символу мага-
зину з використанням правила виведення з номе-
ром i.
o E – Помилка (error); аналізатор зупиняється.
Означення 5. Конфігурація LL(1)-аналізатора – (y, u, w), де:
y – нерозпізнана частина слова x (вхід);
u – магазин;
135
Глибовець М. М., Кирієнко О. В., Проценко В. С.
136
Моделі обчислень у програмній інженерії
137
Глибовець М. М., Кирієнко О. В., Проценко В. С.
138
Моделі обчислень у програмній інженерії
139
Глибовець М. М., Кирієнко О. В., Проценко В. С.
140
Моделі обчислень у програмній інженерії
3. Java
3.1. Регулярні вирази
3.1.1. Регулярні вирази Java
Тексти всіх Java-програм, наведених у розділі 3, можна
знайти в репозиторії https://github.com/ProtsenkoVS/models-
_java.git.
Регулярний вираз – це рядок, побудований за певними пра-
вилами, що задає деяку регулярну мову – множину рядків.
У Java регулярний вираз називають шаблоном рядків. А
мова, що задається регулярним виразом – це рядки, що
відповідають заданому шаблону.
Більшість символів у регулярному виразі, крім зарезервова-
них, позначають себе самі. Символи . * + ? { ( ) [ \ ^ $ заре-
зервовані.
Для позначення такого символу його потрібно вживати
разом із символом \ («екранувати»).
Загалом \c позначає символ c для довільного символу,
крім [A-Za-z0-9].
Можна створювати клас символів, який показує, який символ
із набору може перебувати в цій позиції:
[abc] – довільний символ a, b або c.
[^abc] – довільний символ, крім символів a, b або c.
[a-z] – довільний символ між a і z, включно з a і z.
[A-Za-z] – символ латинського алфавіту.
Є багато зумовлених (визначених) класів символів:
\d – всі десяткові цифри, еквівалентно [0-9].
\D – всі символи, крім десяткових цифр, еквівалентно
[^0-9].
\s – всі «проміжки», еквівалентно [\t\n\r\f\x0B].
\S – всі «не проміжки», еквівалентно [^ \t\n\r\f\x0B].
\w – символи, що вживаються в ідентифікаторах, еквіва-
лентно [0-9_A-Za-z].
У регулярних виразах уживаються:
141
Глибовець М. М., Кирієнко О. В., Проценко В. С.
142
Моделі обчислень у програмній інженерії
143
Глибовець М. М., Кирієнко О. В., Проценко В. С.
144
Моделі обчислень у програмній інженерії
while (mch.find())
System.out.println("e-mail: " + mch.group() );
}
Метод findEmail(input) шукає в рядку input усі поштові
адреси і виводить їх.
145
Глибовець М. М., Кирієнко О. В., Проценко В. С.
146
Моделі обчислень у програмній інженерії
int i = 0;
do{
t = parseInt(sInt[i++]);
if(t==null) r=null; else r +=t;
} while ((r!=null)&& (i<sInt.length));
return r;
}
147
Глибовець М. М., Кирієнко О. В., Проценко В. С.
double coef = 1;
for(int j=1; j<f.length(); j++){
coef /= 10;
rd += (f.charAt(j)-'0') * coef;
}
}
if (p!=null){ // додаємо степінь
String pi = p.substring(1);
if(p.charAt(1)=='+' || p.charAt(1)=='-') pi=p.substring(2);
double pow = 1;
int pw = 0;
for(int k=0; k<pi.length(); k++)
pw = pw*10+(pi.charAt(k)-'0');
for(int k=0; k<pw; k++) pow = pow*10;
if(p.charAt(1)=='-') rd /= pow; else rd *= pow;
}
r = rd;
}
return r;
}
3.1.6. Задачі
1. Побудуйте регулярний вираз у мові Java, що задає:
Непорожню послідовність символів a і/або b.
Ціле десяткове число, можливо, зі знаком + або -.
Ідентифікатор – непорожня послідовність букв латинсь-
кого алфавіту, десяткових цифр і символу _, що почи-
нається з букви.
2. Напишіть наступні статичні методи, використовуючи лише
методи класу String:
static boolean isFromab(String str), який перевіряє, чи ря-
док str – непорожня послідовність символів a і/або b;
static int cntFromab(String str), який підраховує, скільки в
рядку str непорожніх підрядків, що складаються з сим-
волів a і/або b;
148
Моделі обчислень у програмній інженерії
149
Глибовець М. М., Кирієнко О. В., Проценко В. С.
150
Моделі обчислень у програмній інженерії
151
Глибовець М. М., Кирієнко О. В., Проценко В. С.
3.2.2. Вивідність
Оскільки всі правила виведення граматики – це список
ArrayList<Production> product, то кожне правило виведення має
унікальний номер 0 ≤ i < product.size(). Довільне лівостороннє
виведення S =>α1=> α2=> … => αn=α деякої сентенціальної
форми α, у якому на кожному кроці 0 ≤ i ≤ n застосовується ji
правило виведення (0 ≤ ji<product.size()), можна задати масивом
ArrayList<Integer>dv, у якогоdv.size()=n та dv.get(i-1) = ji для
довільного 0 ≤ i<n.
public boolean leftDerivation(ArrayList<Integer>dv){
boolean r=true;
Production pr;
ArrayDeque<Character>sb = new ArrayDeque<>();
int i=0;
sb.push(start);
while(r&&i<dv.size() ){
r = (dv.get(i)>=0 &&dv.get(i) <product.size() && !sb.isEmpty());
if(r){
pr=product.get(dv.get(i++));
r = pr.getNon().equals(sb.peek());
152
Моделі обчислень у програмній інженерії
if(r){
sb.pop();
for(int j=pr.getRull().length(); j>0; j--){
Character c = pr.getRull().charAt(j-1);
if (Character.isUpperCase(c))sb.push(c);
}
}
}
}
return r;
}
Метод leftDerivation(dv) перевіряє, чи масив dv задає деяке
лівостороннє виведення в заданій граматиці (екземпляр об’єкта
Grammar). Метод використовує стек sb, що містить усі не-
термінальні символи, які з’являються в процесі цього лівосто-
роннього виведення в оберненому порядку.
На початку стек містить початковий нетермінал start.
Для кожного кроку виведення 0 ≤ i<dv.size() верхній еле-
мент стека – це лівий нетермінал правила виведення, що
застосовується на цьому кроці pr.getNon().equals-
(sb.peek()), який необхідно замінити на всі нетермінали,
що трапляються у правій частині правила виведення в
оберненому порядку.
153
Глибовець М. М., Кирієнко О. В., Проценко В. С.
root=c; sons=null;
}
public void addSon(SynTree s){
if (sons==null) sons = new ArrayList<SynTree> ();
sons.add(s);
}
public Character getRoot() {return root;}
publicList<SynTree> getSons() {return sons;}
public String toString() {
if (sons==null)return(root==null?"Eps":String.valueOf(root));
StringBuilder buf = new StringBuilder();
buf.append('('); buf.append(root);
for (int i=0; i<sons.size(); i++){
buf.append(' ');
buf.append(sons.get(i).toString());
}
buf.append(')');
return buf.toString();
}
}
Конструктор SynTree() будує дерево-листок, у якого обидва
поля root i sons дорівнюють null. Це синтаксичне дерево, що
відповідає листку, поміченому символом ε (порожній рядок).
Воно утворюється після застосування правила виведення A –>ε.
Конструктор SynTree(char c) будує дерево-листок, корінь
якого містить символ с (термінал або нетермінал).
Додати синтаксичне дерево-нащадок e можна за допомогою
методу addSon (SynTreee).
public SynTree buildSynTree(ArrayList<Integer>dv){
SynTree tr = null;
if(leftDerivation(dv)){
tr = new SynTree(start);
buildInDepth(tr,0,dv);
}
return tr;
}
154
Моделі обчислень у програмній інженерії
155
Глибовець М. М., Кирієнко О. В., Проценко В. С.
156
Моделі обчислень у програмній інженерії
private Map<String,Integer>test;
private Map<Character,Set<Character>>fst, nxt;
157
Глибовець М. М., Кирієнко О. В., Проценко В. С.
si.add(r);
terrors.put(key, si);
}
…….
}
Предикат isLL1()перевіряє, чи є граматика LL(1)-граматикою.
Основна частина методу isLL1() зводиться до побудови двох
відображень (таблиць):
Map<String, Set<Integer>> terrors;
Map<String, Integer> test;
Ключами кожного відображення є рядки “Nt”, де N – не-
термінал і t – термінал граматики.
Якщо граматика є LL(1)-граматикою, то відображення
terrors – порожнє, а відображення test – це керівна таблиця
LL(1)-аналізатора.
Якщо в граматиці існують правила A –>β (із номером k)
та A –>γ (із номером j) такі, що S*=lm>wAα і t first(βα)
first(α), то відображення terrors має ключ “At” і k, j
terrors.get(“At”), а test.get(“At”)== -1.
Для цього перебирають усі правила виведення pr =
product.get(i) граматики, і для нетермінала n = pr.getNon(), із
використанням функцій first і follow, визначаються всі терміна-
льні символи c, що можуть з’явитися першими в лівосторон-
ньому виведенні, який використовує правило виведення pr.
Метод add(N,t,i) зв’язує із ключем key = “”+N+t правило ви-
ведення i та робить спробу додати цю пару у відображення test.
Якщо в цьому відображенні із ключем key уже зв’язане інше
значення j>=0 або -1, то граматика не являє собою LL(1)-грама-
тику. В останньому випадку ключ key додається у відображення
terrors, а у відображенні test із ключем key зв’язується значе-
ння -1.
158
Моделі обчислень у програмній інженерії
3.3.2. LL(1)-аналіз
Метод analys(input) визначає, чи належить слово input мові,
що задається граматикою. Якщо слово input належить мові, то
повертається його лівостороннє виведення ArrayList<Integer>
dv != null, а в іншому разі – null.
public ArrayList<Integer> analys(String input){
ArrayList<Integer>dv = new ArrayList<> ();
String wd = input + '$';
String stack = "" + start + '$';
while(stack.charAt(0)!='$'){
Character r = stack.charAt(0);
stack = stack.substring(1);
if(nonterminals.contains(r)){
String key = ""+r+wd.charAt(0);
if(test.containsKey(key)){
Integer j = test.get(key);
dv.add(j);
stack = product.get(j).getRull() + stack;
} else return null;
} else
if(r.equals(wd.charAt(0)))wd=wd.substring(1); else return null;
}
if (wd.charAt(0)!='$') return null;
return dv;
}
Метод просто моделює роботу LL(1)-аналізатора, використо-
вуючи відображення test як керівну таблицю σ. Компоненти
конфігурації відповідно реалізують змінні:
String wd – вхід y – нерозпізнана частина слова input;
String stack – магазин u;
ArrayList<Integer>dv – виведення w – список номерів
правил виведення.
159
Глибовець М. М., Кирієнко О. В., Проценко В. С.
160
Моделі обчислень у програмній інженерії
Boolean go = true;
int i=0;
while (i<rull.length() &&go){
Set<Character>cs = fstp.get((Character)rull.charAt(i));
go = cs.contains('$');
addWithoutEps(ns,cs);
i++;
}
if (i==rull.length() &&go) ns.add('$');
fst.put(n,ns);
}
}
}
161
Глибовець М. М., Кирієнко О. В., Проценко В. С.
if(i<rull.length()-1){
Set ntc = fst.get((Character)rull.charAt(i+1));
addWithoutEps(cs,ntc);
Boolean go = ntc.contains('$');
int j=i+2;
while(j<rull.length() &&go){
ntc = fst.get((Character)rull.charAt(j));
addWithoutEps(cs,ntc);
go = ntc.contains('$');
j++;
}
if(go)cs.addAll(nxtp.get(n));
}
else cs.addAll(nxtp.get(n));
}
i++;
}
}
}
}
162
Моделі обчислень у програмній інженерії
if(fst.get(c).contains('$')){
addWithoutEps(rs,fst.get(c));
rs.addAll(first(wd.substring(1)));
} else rs = fst.get(c);
}
else rs = fst.get(c);
}
else rs.add('$');
return rs;
}
163
Глибовець М. М., Кирієнко О. В., Проценко В. С.
164
Моделі обчислень у програмній інженерії
public ParserG(){}
public boolean analys(String word){
input = new Letter(word);
try{
next = input.nextChar();
S(); match('$');
} catch(SyntaxError ex){
System.out.println(ex.getMessage());
return false;
}
return true;
}
165
Глибовець М. М., Кирієнко О. В., Проценко В. С.
}
public class SyntaxError extends Exception {
public SyntaxError(String msg) { super(msg);}
}
166
Моделі обчислень у програмній інженерії
167
Глибовець М. М., Кирієнко О. В., Проценко В. С.
168
Моделі обчислень у програмній інженерії
else E();
}
void E() throws SyntaxError{
T(); A();
}
void A() throws SyntaxError{
if(next=='+'){
next=input.nextChar(); T(); A();
} else if(next=='-'){
next=input.nextChar(); T(); A();
}
}
void T() throws SyntaxError{
F(); B();
}
void B() throws SyntaxError{
if(next=='*'){
next=input.nextChar(); F(); B();
} else if(next=='/'){
next=input.nextChar(); F(); B();
} else if(next=='%'){
next=input.nextChar(); F(); B();
}
}
void F() throws SyntaxError{
if (next=='(' ){
next=input.nextChar(); S(); match(')');
} else if(next<='9'&&next>='0')
next=input.nextChar();
else throw new SyntaxError(
"Expecting one from \"0123456789(\", found " + next);
}
void match(char c) throws SyntaxError{
if(next==c) next=input.nextChar();
else throw new SyntaxError("Expecting " + c + ", found " + next);
}
}
169
Глибовець М. М., Кирієнко О. В., Проценко В. С.
170
Моделі обчислень у програмній інженерії
171
Глибовець М. М., Кирієнко О. В., Проценко В. С.
172
Моделі обчислень у програмній інженерії
173
Глибовець М. М., Кирієнко О. В., Проценко В. С.
174
Моделі обчислень у програмній інженерії
175
Глибовець М. М., Кирієнко О. В., Проценко В. С.
3.4.5. Задачі
1. Мова містить усі слова, що мають баланс круглих дужок,
що відкривають і закривають. Тобто кожне слово мови
або порожнє, або будь-яка пара дужок розділяє його на
три частини α, β, γ: α’(‘β’)’γ, і кожна з частин α, β, γ,
своєю чергою, має баланс дужок. Алфавіт мови T = {‘(‘,
’)’}. КВ граматика, що задає цю мову G = ({S}, {(, )}, {S
–> (S)S, S –> ε}, S). Побудувати клас Balance, який
використовує рекурсивний спуск і має відкриті методи:
boolean analys(String word), що реалізує синтаксич-
ний аналіз мови;
176
Моделі обчислень у програмній інженерії
177
Глибовець М. М., Кирієнко О. В., Проценко В. С.
178
Моделі обчислень у програмній інженерії
179
Глибовець М. М., Кирієнко О. В., Проценко В. С.
180
Моделі обчислень у програмній інженерії
public Parser() {}
public boolean synAnalys(String src){
try{
input = new Lexer(new Letter(src));
next = input.nextToken();
expr(); match(Lexer.EOFT);
} catch(SyntaxError ex){
181
Глибовець М. М., Кирієнко О. В., Проценко В. С.
182
Моделі обчислень у програмній інженерії
183
Глибовець М. М., Кирієнко О. В., Проценко В. С.
} catch(Exception e) {
System.out.println(
">>>: SrcFile: openfile" + name + ": " + e.getMessage());
input = null; c=EOF;
}
}
public char nextChar() {
consume(); return c;
}
private void inputLine(){
if (r!=null) {
try {
input = r.readLine(); p=0; c=' ';
if (input==null) c=EOF;
} catch(Exception e) {
System.out.println(">>>: SrcFile: inputLine " +
e.getMessage()); }
} else c=EOF;
}
private void consume() {
if (input != null){
if ( !(p<input.length()) ) inputLine();
} else c= EOF;
if (c!=EOF) c = input.charAt(p++);
}
}
Інтерфейс Letter містить константу EOF, що задає кінець по-
току символів, і єдиний метод nextChar(), що повертає наступний
символ потоку.
Клас SrcString(Stringinput) реалізує інтерфейс, задаючи дже-
релом потоку символів рядок input (фактично це модифікація
попереднього класу Letter).
Клас SrcFile(Stringname) реалізує інтерфейс, задаючи джере-
лом потоку символів файл name.
Зміни в класі Parser мінімальні – це поява двох методів, ко-
жний із яких створює екземпляр класу SrcFile або SrcString, що
184
Моделі обчислень у програмній інженерії
boolean synAnalys(){
try{
next = input.nextToken();
expr();match(Lexer.EOFT);
} catch(SyntaxError ex){
System.out.println("----Syntax ERROR: " + ex.getMessage());
return false;
}
return true;
}
……..
}
185
Глибовець М. М., Кирієнко О. В., Проценко В. С.
186
Моделі обчислень у програмній інженерії
buf.append(sons.get(i).toString());
}
buf.append(')');
return buf.toString();
}
// обчислює AST, що задає арифметичний вираз
public int evalExpr(){
int res,op1,op2;
switch(root.type){
case Lexer.ADDOP:
op1=sons.get(0).evalExpr();
if (sons.size()==2){
op2=sons.get(1).evalExpr();
if (root.text.equals("-")) res=op1-op2; else res=op1+op2;
} else res=-op1;
break;
case Lexer.MULOP:
op1=sons.get(0).evalExpr();
op2=sons.get(1).evalExpr();
if (!root.text.equals("*")){
if(op2==0)
throw new Error("В операції " + root.text + " дільник =
0");
if (root.text.equals("/")) res=op1/op2; elseres=op1%op2;
} else res= op1*op2;
break;
case Lexer.NUMB: res=evalNumber(root.text); break;
default: throw new Error("Неочікувана лексема " +
root.toString());
}
return res;
}
private int evalNumber(String numb){
int res=0;
for(int j=0; j<numb.length(); j++)
res = res*10+(numb.charAt(j)-'0');
return res;
} }
187
Глибовець М. М., Кирієнко О. В., Проценко В. С.
public ParserAST() { }
public AST analysFile(String file){
input = new Lexer(new SrcFile(file));
return synAnalys();
}
public AST analysStr(String src){
input = new Lexer(new SrcString(src));
return synAnalys();
}
AST synAnalys(){
AST t;
try{
next = input.nextToken();
t=expr(); match(Lexer.EOFT);
} catch(SyntaxError ex){
System.out.println("----Syntax ERROR: " +
ex.getMessage());
return null;
}
return t;
}
AST expr() throws SyntaxError{
AST t = null,t1;
if(next.type==Lexer.ADDOP) {
if (next.text.equals("-")) t= new AST(next);
next=input.nextToken();
}
if (t==null) t=term(); else t.addSon(term());
while (next.type==Lexer.ADDOP){
t1 = new AST(next); next=input.nextToken();
188
Моделі обчислень у програмній інженерії
3.6. Лямбда-вирази
3.6.1. Потоки даних
Потік даних – це послідовність, можливо, нескінченна, да-
них одного типу. Всі елементи одного потоку – або екземпляри
189
Глибовець М. М., Кирієнко О. В., Проценко В. С.
190
Моделі обчислень у програмній інженерії
191
Глибовець М. М., Кирієнко О. В., Проценко В. С.
192
Моделі обчислень у програмній інженерії
193
Глибовець М. М., Кирієнко О. В., Проценко В. С.
194
Моделі обчислень у програмній інженерії
class Pair {
int f, s;
Pair(int fi, int si){
f=fi; s=si;
}
195
Глибовець М. М., Кирієнко О. В., Проценко В. С.
196
Моделі обчислень у програмній інженерії
197
Глибовець М. М., Кирієнко О. В., Проценко В. С.
198
Моделі обчислень у програмній інженерії
3.6.6. Бібліотека
Кожен екземпляр класу Book задає інформацію про одну
книжку бібліотеки:
Список авторів книжки – ArrayList<String> authors.
Назва книжки – String titleName.
Об’єм книжки, кількість сторінок – int pages.
Множина жанрів, у які потрапляє книжка – Set<String>
types.
Назва видавництва – String pubName.
Для доступу до кожного з атрибутів клас має відповідний
селектор get…
public class Book {
ArrayList <String>authors;
String titleName;
int pages;
Set <String>types;
String pubName;
199
Глибовець М. М., Кирієнко О. В., Проценко В. С.
200
Моделі обчислень у програмній інженерії
201
Глибовець М. М., Кирієнко О. В., Проценко В. С.
Collectors.groupingBy(book->book.getPubName(), //
Book::getPubName
Collectors.mapping(b->b.getTitleName(), //Book::getTitleName,
Collectors.toList())));
}
Методи nameOfBooksC(library) і nameOfBooksS(library)
формують відображення (таблицю), у якому всі книжки бібліо-
теки класифіковані за видавництвами, що їх видало.
Для формування заключного відображення використовується
ітератор або понижувальний колектор.
202
Моделі обчислень у програмній інженерії
203
Глибовець М. М., Кирієнко О. В., Проценко В. С.
BufferedImage.TYPE_INT_RGB)
Для запису в буфер image інформації про піксель з
координатами x,y і кольором rgb використовується метод
setRGB(int x, inty, int rgb).
Для запису образу у файл використовується статичний
метод write класу ImageIO ImageIO.write(image, format,
file):
o image – графічний образ – екземпляр класу
BufferedImage;
o format – формат вихідного файлу “png”;
o file – файл, куди потрібно записати графічний
образ у потрібному форматі, – екземпляр класу
File. Якщо nmFile – ім’я файлу, то файл – екзем-
пляр класу File може створити конструктор
newFile(nmFile).
public class Mandelbrot {
int width = 2*1920;
int height = 2*1080;
BufferedImage image =
204
Моделі обчислень у програмній інженерії
public Mandelbrot(){
for (int i = 0; i<max; i++) {
colors[i] = Color.HSBtoRGB(i/256f, 1, i/(i+8f));
}
}
205
Глибовець М. М., Кирієнко О. В., Проценко В. С.
206
Моделі обчислень у програмній інженерії
207
Глибовець М. М., Кирієнко О. В., Проценко В. С.
3.6.8. Задачі
1. Написати статичний метод, використовуючи IntStream:
int sumA(int[] ia), що знаходить суму всіх елементів ма-
сиву ia;
int[] onlyPos(int[] ia), що формує за масивом ia новий
масив, що включає лише додатні елементи масиву ia;
IntStream factS(), що формує нескінченний потік фак-
торіалів додатних натуральних чисел;
OptionalInt maxPos(IntStream is), що знаходить у потоці
is максимальне парне додатне число.
2. Написати статичний метод, використовуючи Stream<String>:
boolean isIn(String[] as, String s), що визначає, чи є в ма-
сиві as елемент, який дорівнює рядку s;
Stream<String> onlyAb(String word), який формує потік
непорожніх підрядків рядка word, що містять лише сим-
воли a і b;
Stream<String> onlyAbBeginA(String word), який формує
потік усіх підрядків рядка word, що починаються з сим-
волу a й містять лише символи a та b;
Optional<String> minAb(String word), який знаходить у
слові word найменший підрядок, що починається з сим-
волу a й містить лише символи a та b. (У слові може не
бути жодного такого підрядка.)
3. Написати статичний метод, використовуючи Stream<String>
і роботу із файлами:
208
Моделі обчислень у програмній інженерії
Список літератури
1. Ахо А., Лам М., Сети Р., Ульман Д. Компиляторы: прин-
ципы, технологии и инструментарий. СПб. : ООО «Диа-
лектика», 2019. 1184 с.
2. Катленд Т. Вычислимость. Введение в теорию рекурсив-
ных функций. Москва : Мир, 1983. 256 с.
3. Коузен К. Современный Java: рецепты программирова-
ния. Москва : ДМК Пресс, 2018. 274 с.
4. Хорстманн Кей С. Java SE 8. Базовый курс. Москва : Изд.
дом «Вильямс», 2016. 464 с.
209
Навчальне видання
Навчальний посібник
Оригінал-макет підготовлений
Видавничим домом «Києво-Могилянська академія»
Адреса видавництва:
04070, м. Київ, Контрактова пл., 4
Тел./факс: (044) 425-60-92
E-mail: realization.ukma@gmail.com
http://www.publish-ukma.kiev.ua
УДК 004.4/.9(075.8)