Upload
others
View
10
Download
0
Embed Size (px)
Citation preview
Київський національний університет
імені Тараса Шевченка
МЕТОДИЧНІ РЕКОМЕНДАЦІЇ
до практичних занять
з курсу «Теорія програмування»
Київ – 2009
2
УДК 681.3
Рецензенти:
Бєлов Ю.А., доктор фізико-математичних наук, професор факультету
кібернетики Київського національного університету імені Тараса Шевченка
Дорошенко А.Ю., доктор фізико-математичних наук, професор, заступник
директора з наукової роботи Інституту програмних систем НАН України
Затверджено Вченою радою факультету кібернетики
Протокол № __ від __ ______ 2010 р.
Нікітченко М.С., Панченко Т.В. Методичні рекомендації до практичних
занять з курсу «Теорія програмування». – Київ, 2010. – ___ с.
Навчальний посібник призначений для студентів спеціальності
інформатика, які проходять курс «Теорія програмування», або споріднений, в
якому розглядаються питання синтаксису, семантики та коректності програм.
В посібнику надаються розв’язки задач, які виникають при розгляді
зазначених питань, а також детальні коментарі та пояснення.
© Нікітченко М.С., Панченко Т.В., 2010.
3
Зміст
Вступ .................................................................................................................................................... 4
1. Синтаксис та семантика. Мова SIPL. Композиційна семантика ............................................... 4
1.1. Синтаксис мови SIPL .............................................................................................................. 4
1.2. Композиційна семантика мови SIPL ..................................................................................... 6
1.2.1. Дані .................................................................................................................................... 6
1.2.2. Функції .............................................................................................................................. 8
1.2.3. Композиції ........................................................................................................................ 9
1.2.4. Програмні алгебри ......................................................................................................... 10
1.3. Побудова семантичного терму програми ........................................................................... 12
1.4. Доведення коректності програм .......................................................................................... 14
1.5. Розв’язки типових задач ....................................................................................................... 14
1.6. Розширення мови SIPL: Введення булевих змінних ......................................................... 43
1.6.1. ............................................................................................................................................... 43
1.7. Приклади розв’язків задач. ................................................................................................... 48
1.8. Розширення мови SIPL: Введення викликів функцій ........................................................ 53
1.9. Приклади задач ...................................................................................................................... 55
1.10. Завдання для самостійної роботи ...................................................................................... 55
1.11. Приклади завдань для контрольної роботи. ..................................................................... 55
Тема 2. Формальні мови та граматики ........................................................................................... 56
Тема 3. Рекурсія та найменша нерухома точка. Неперервність операторів ............................... 63
Тема 4. Натуральна семантика ........................................................................................................ 66
Тема 5. Аксіоматична семантика .................................................................................................... 75
Література ......................................................................................................................................... 81
4
Вступ
Теорія програмування - …
Доведення коректності програм
…
семантика
аспекти програм
методи, способи …
побудова і доведення (+ за побудовою)
формалізація, математичні формальні підходи до: …
1. Синтаксис та семантика. Мова SIPL. Композиційна семантика
1.1. Синтаксис мови SIPL
Для побудови дерева синтаксичного виводу, композиційного
семантичного терму і доведення коректності програми ми будемо
використовувати мову SIPL. Говорячи неформально, мова SIPL має змінні цілого
типу, над якими будуються арифметичні вирази та умови. Основними
операторами є присвоєння, послідовне виконання, розгалуження, цикл.
Мова SIPL може розглядатися як надзвичайно спрощена традиційна мова
програмування, наприклад, Паскаль. В мові SIPL відсутні типи, оператори вводу-
виводу та багато інших конструкцій традиційних мов програмування. Разом з
тим ця мова є досить потужною, щоб програмувати різні арифметичні функції,
більше того, в цій мові можуть бути запрограмовані всі обчислювальні функції
над цілими числами.
Для опису синтаксису мов зазвичай використовують БНФ – форми
Бекуса-Наура. Програми або їх частини виводяться із метазмінних
(нетерміналів), які записуються у кутових дужках. Метазмінні задають
синтаксичні класи. В процесі виводу метазмінні замінюються на праві частини
правил, що задають ці метазмінні. Праві частини для однієї метазмінної
розділяються знаком альтернативи « | ». Процес породження припиняється,
якщо всі метазмінні замінено на термінальні символи.
Синтаксис мови SIPL можна задати за допомогою наступної БНФ:
5
Таблиця 1.1
Ліва частина
правила –
метазмінна
(дефінієндум)
Права частина правила
(дефінієнс)
Ім’я
правила
<програма> ::= begin <оператор> end NP1
<оператор> ::=
<змінна>:=<вираз> |
<оператор> ; <оператор>|
if <умова> then <оператор> else <оператор> |
while <умова> do <оператор> |
begin <оператор> end |
skip
NS1
NS2
NS3
NS4
NS5
NS6
<вираз> ::= <число> | <змінна> | <вираз> + <вираз> |
<вираз> – <вираз> | <вираз> * <вираз> |
<вираз> ÷ <вираз> | (<вираз>)
NA1–
NA7
<умова> ::= true | false | <вираз> < <вираз> |
<вираз> <вираз> | <вираз> = <вираз> |
<вираз> <вираз> | <вираз> > <вираз> |
<вираз> <вираз> | <умова> <умова> |
<умова> <умова> | <умова> | (<умова>)
NB1–
– NB12
<змінна> ::= M | N | . . . NV1–…
<число> ::= . . . –1 | 0 | 1 | 2 | 3 | . . . NN1–…
Наведена БНФ задає мову SIPL як набір речень (слів), які виводяться з
метазмінної <програма>. Щоб переконатись у синтаксичній правильності
програми треба побудувати її вивід, який можна подати у вигляді дерева.
6
1.2. Композиційна семантика мови SIPL
Семантика задає значення (смисл) програми. Наш приклад показує, що
смисл програми – перетворення вхідних даних у вихідні. В математиці такі
перетворення називають функціями. Тому до семантичних понять відносять
поняття даних, функцій та методів їх конструювання. Такі методи називаються
композиціями, а відповідна семантика часто називається композиційною.
Будемо вживати термін композиційна семантика, тому що саме композиції і
визначають її властивості. Композиційна семантика є певною конкретизацією
функціональної семантики, бо базується на тлумаченні програм як функцій.
1.2.1. Дані
Базові типи даних – множини цілих чисел, бульових значень та змінних
(імен):
Int={ . . . , -2, -1, 0, 1, 2, . . . }
Bool={true, false}
Var={X, Y, … }
Похідні типи – множина станів змінних (наборів іменованих значень,
наборів змінних з їх значеннями):
State=Var Int
Приклади станів змінних: [M8, N16], [M3, X 4, Y2, N16].
Операції на базових типах даних. Щоб відрізнити операції від символів,
якими вони позначаються, будемо записувати їх жирним шрифтом, або писати
спеціальні позначення. Ми тут оберемо другий варіант, вводячи нові
позначення для операцій на типах даних.
Операції на множині Int. Символам +, – , , відповідають операції add,
sub, mult, div (додавання, віднімання, множення, ділення). Це бінарні операції
типу Int2 Int.
Операції на множині Bool. Символам , , відповідають операції or,
and, neg. Це бінарні операції типу Bool2 Bool (диз’юнкція та кон’юнкція) та
унарна операція типу Bool Bool (заперечення).
7
В мові також є операції змішаного типу. Символам операцій порівняння
<, , =, , , > відповідають операції less, leq, eq, neq, geq, gr. Це бінарні
операції типу Int2 Bool.
Для опису мови SIPL треба додати ще одну основу – множину станів
змінних. На цій основі задана бінарна операція накладання (для цієї операції
іноді вживається термін накладка). Ця операція за двома станами будує новий
стан змінних, до якого входять всі іменовані значення з другого стану, та ті
значення з першого стану, імена яких не входять до другого стану. Ця операція
подібна до операції копіювання каталогів з спеціальним випадком однакових
імен файлів у двох каталогах. В цьому випадку файл з першого каталогу
замінюється файлом з тим же іменем з другого каталогу.
Наприклад:
[M8, N16] [M3, X 4, Y2]= [M3, N16, X 4, Y2].
Введемо відношення розширення станів новими іменованими
значеннями (іменними парами). Наприклад,
[M8, N16] [M8, X 4, Y2, N16].
Для створення та оперування із станами змінних треба визначити дві
функції: іменування x: Int State та розіменування x: State Int, які мають
параметр xVar:
x(n)=[xn]
x(st)=st(x)
Тут і в подальшому вважаємо, що nInt, stState. Перша функція іменує
іменем x число n, створюючи стан [xn], друга бере значення імені x в стані st.
Сама формула ґрунтується на тому факті, що стани змінних можуть
тлумачитись як функції виду VarInt. Функція розіменування є частковою.
Вона не визначена, якщо x не має значення в стані st.
Наприклад:
M(5)=[M5],
Y([M3, X4, Y2])=2,
8
Y([M3, X4]) не визначене.
Крім того, введемо
функцію-константу арифметичного типу n : StateInt, таку що
n (st)=n,
функції-константи бульового типу true , false : StateBool такі, що
true (st)=true, false (st)=false,
тотожну функцію id: StateState, таку, що id(st)=st.
Отримали багатоосновну алгебру даних мови SIPL
A_Int_Bool_State =<Int, Bool, State; add, sub, mult, div, or, and, neg,
less, leq, eq, neq, geq, gr, x, x, n , id, , true , false >.
1.2.2. Функції
Аналіз алгебри даних показує, що в мові SIPL можна вирізнити два види
функцій: 1) n-арні функції на базових типах даних, та 2) функції над станами
змінних. Другий вид функцій будемо називати номінативними функціями.
Назва пояснюється тим, що вони задані на наборах іменованих даних
(латинське nomen – ім’я).
Визначимо тепер наступні класи функцій, які будуть задіяні при
визначенні семантики мови SIPL:
1. n-арні операції над базовими типами:
FNA=Intn Int – n-арні арифметичні функції (операції),
FNB=Booln Bool – n-арні бульові функції (операції),
FNAB=Intn Bool – n-арні функції (операції) порівняння.
2. Функції над станами змінних
FA=State Int – номінативні арифметичні функції,
FB=State Bool – номінативні предикати,
FS=State State – біномінативні функції –перетворювачі
(трансформатори) станів.
Для операцій мови SIPL зазвичай n=2, а для бульової операції
заперечення n=1.
9
1.2.3. Композиції
Композиції формалізують методи побудови програм. Аналіз мови SIPL
дає підстави говорити про те, що будуть вживатися композиції різних типів, а
саме:
композиції, які пов’язані з номінативними функціями та предикатами,
композиції, пов’язані з біномінативними функціями.
Перший клас композицій використовується для побудови семантики
арифметичних виразів та умов, другий – операторів.
Цей клас композицій складається з композицій суперпозиції в n-арні
функції, які задані на різних основах (класах функцій):
суперпозиція номінативних арифметичних функцій в n-арну
арифметичну функцію має тип Sn: FNAFA
nFA,
суперпозиція номінативних арифметичних функцій в n-арну фунцію
порівняння має тип Sn: FNABFA
nFB,
суперпозиція номінативних предикатів в n-арну бульову фунцію має
тип Sn: FNBFB
nFB.
Зауваження: суперпозиції різного типу позначаємо одним знаком.
Суперпозиція задається формулою (тут f – n-арна функція, g1,…,gn –
номінативні функції відповідного типу):
(Sn(f, g1,…,gn ))(st)=f(g1(st),…,gn(st)).
Другий клас композицій складається з наступних композицій.
Присвоєння ASx: FA FS (x – параметр).
Присвоєння задається формулою:
ASx (fa)(st)=st [x fa(st)]
Послідовне виконання : FS2FS
Послідовне виконання задається формулою:
(fs1fs2)(st)=fs2(fs1(st))
Умовний оператор (розгалуження): IF:FBFS2FS. Задається
формулою:
10
.якщо,
,якщо),())(,,(
2
1
21falsefb(st)(st)fs
truefb(st)stfsstfsfsfbIF
Цикл (ітерація з передумовою): WH:FBFSFS. Задається рекурентно
(індуктивно) наступним чином:
WH(fb,fs)(st)=stn, де
st0=st, st1=fs(st0), st2=fs(st1),…, stn=fs(stn-1), причому
fb(st0)=true, fb(st1)=true,…, fb(stn-1)=true, fb(stn)=false.
Важливо відзначити, що для циклу наведена послідовність визначається
однозначно. Позначимо число n (кількість ітерацій циклу) як NumItWH((fb, fs),
st). Однозначність визначення n дозволяє розглядати NumItWH((fb, fs), st) як
тернарне відображення, яке залежить від fb, fs, st. Якщо цикл не завершується,
то NumItWH((fb, fs), st) вважається невизначеним. Це відображення буде
використовуватись в індуктивних доведеннях властивостей циклу.
1.2.4. Програмні алгебри
Побудовані композиції дозволяють стверджувати, що отримано алгебру
функцій (програмну алгебру)
A_Prog =<FNA, FNB, FNAB, FA, FB, FS; Sn, AS
x, , IF, WH, x, id >.
Наведена алгебра містить багато «зайвих» функцій, які не можуть бути
породжені в мові SIPL. Аналіз мови дозволяє стверджувати (цей факт буде
доведено пізніше в теоремі 1.1), що функції, які задаються мовою SIPL,
породжуються в алгебрі A_Prog з наступних базових функцій:
add, sub, mult, div FNA,
or, and, neg FNB,
less, leq, eq, neq, geq, gr FNAB,
nFA (nInt),
true , falseFB,
Підалгебру алгебри A_Prog, породжену наведеними базовими функціями,
назвемо функціональною алгеброю мови SIPL і позначимо A_SIPL.
11
Зауважимо, що всі функції алгебри A_SIPL є однозначними
(детермінованими) функціями. Це випливає з того, що всі базові функції є
однозначними, а композиції зберігають цю властивість. (Довести цю
властивість самостійно).
Формули для обчислення композицій та функцій алгебри A_Prog подамо
у наступній таблиці (тут f – n-арна функція, fa, g1,…, gn – номінативні
арифметичні функції, fb – номінативний предикат, fs, fs1, fs2 – біномінативні
функції, st – стан, n – число).
Таблиця 1.2
Композиція
(функція)
Формула обчислення
Ім’я
формули
Суперпозиція (Sn(f, g1,…, gn))( st)=f(g1(st),…, gn(st)) AF_S
Присвоєння ASx (fa)(st)=st [x fa(st)] AF_AS
Послідовне
виконання
fs1fs2(st)=fs2(fs1(st)) AF_SEQ
Умовний
оператор
.якщо,2
,якщо),(1))(2,1,(
falsefb(st)(st)fs
truefb(st)stfsstfsfsfbIF AF_IF
Цикл WH(fb,fs)(st)=stn, де
st0=st, st1=fs(st0), st2=fs(st1), …, stn=fs(stn-1),
причому fb(st0)=true, fb(st1)=true,…,
fb(stn-1)=true, fb(stn)=false.
AF_WH
Функція
розіменування
x(st)=st(x) AF_DNM
Тотожна функція id(st)=st AF_ID
Побудована алгебра A_SIPL дозволяє тепер формалізувати семантику
програм мови SIPL, задаючи їх функціональними виразами (семантичними
термами) алгебри A_SIPL.
12
1.3. Побудова семантичного терму програми
Програма мови SIPL може бути перетворена в семантичний терм (терм
програмної алгебри), який задає семантику цієї програми (семантичну функцію
програми), перетвореннями такого типу:
sem_P: Prog FS
sem_S: Stm FS
sem_A: Aexp FA
sem_B: Bexp FB
Ці перетворення задаються рекурсивно (за структурою програми). Тому
побудова семантичного терму залежить від вибору структури синтаксичного
запису програми. Тут треба зважати на неоднозначність обраної нами
граматики, що може призвести до різної семантики програм. Для досягненя
однозначності треба користуватись пріоритетами операцій та типом їх
асоціативності.
Таблиця 1.3
Правило заміни Назва правила
sem_P: Prog FS задається правилами:
sem_P(begin S end)= sem_S(S) NS_Prog
sem_S: Stm FS задається правилами:
sem_S(x:=a)=ASx(sem_A(a))
sem_S(S1;S2)= sem_S(S1) sem_S(S2)
sem_S(if b then S1 else S2)= IF(sem_B(b), sem_S(S1),
sem_S(S2))
sem_S(while b do S)= WH(sem_B(b), sem_S(S))
sem_S(begin S end)=(sem_S(S))
NS_Stm_As
NS_Stm_Seq
NS_Stm_If
NS_Stm_Wh
NS_Stm_Op
13
sem_S(skip)=id NS_Stm_skip
sem_A: Aexp FA задається правилами:
sem_A(n)= n
sem_A(x)=x
sem_A(a1+a2)=S2(add, sem_A(a1), sem_A(a2))
sem_A(a1–a2)=S2(sub, sem_A(a1), sem_A(a2))
sem_A(a1a2)=S2(mult, sem_A(a1), sem_A(a2))
sem_A(a1a2)=S2(div, sem_A(a1), sem_A(a2))
sem_A((a))=sem_A(a)
NS_A_Num
NS_A_Var
NS_A_Add
NS_A_Sub
NS_A_Mult
NS_A_Div
NS_A_Par
sem_B: Bexp FB задається правилами:
sem_B(true)= true
sem_B(false)= false
sem_B(a1<a2)=S2(less, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(leq, sem_A(a1), sem_A(a2))
sem_B(a1=a2)=S2(eq, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(neq, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(geq, sem_A(a1), sem_A(a2))
sem_B(a1>a2)=S2(gr, sem_A(a1), sem_A(a2))
sem_B(b1b2)=S2(or, sem_B(b1), sem_B(b2))
sem_B(b1b2)=S2(and, sem_B(b1), sem_B(b2))
sem_B(b)=S1(neg, sem_B(b))
sem_B((b))= sem_B(b)
NS_B_true
NS_B_false
NS_B_less
NS_B_leq
NS_B_eq
NS_B_neq
NS_B_geq
NS_B_gr
NS_B_or
NS_B_and
NS_B_neg
NS_B_Par
Наведені правила слід розглядати як загальні правила, які в логіці
називають схемами правил. Щоб із загального правила (метаправила) отримати
конкретне правило (об’єктне правило), слід замість синтаксичних
14
метасимволів, таких як a, b, S, P, n, x, підставити конкретні синтаксичні
елементи (записи), наприклад замість a підставити N–M, замість b – M>N і т.п.
Далі ліва частина конкретного правила заміняються на його праву частину і т.д.
1.4. Доведення коректності програм
Доведення коректності програм можна проводити різними способами,
зокрема – див. розділ 5: аксіоматична семантика. Головна задача – показати
часткову та/або тотальну коректність програми, тобто що вона на довільних
вхідних даних видає правильні, заздалегідь очікувані, результати (такі, що
відповідають початковій формальній специфікації програми). Одним з підходів
до доведення часткової коректності програм у композиційній семантиці є
обчислення значення семантичного терму програми над деяким узагальненим
даним d, що має вигляд пар ім’я – значення без конкретизації фіксованих
значень змінних, тобто sem_P(P) (d), та порівняння отриманого даного
(точніше – окремих його компонент-значень змінних) з очікуваним згідно
формальної специфікації. Тотальна коректність доводиться шляхом доведення
завершуваності програми. Останнє найчастіше зводиться до доведення
завершуваності циклів, що входять до програми.
1.5. Розв’язки типових задач
Завдання 1.1. За допомогою алгоритму Евкліда знайти найбільший спільний
дільник двох чисел GCD(M,N). Виконати:
1) побудувати програму для розв’язання задачі на мові SIPL,
2) побудувати дерево синтаксичного виводу програми,
3) побудувати композиційний семантичний терм програми,
4) обчислити значення семантичного терму над вхідним даним
[M->15,N->9].
Розв’язок.
15
Алгоритм Евкліда для пошуку найбільшого спільного дільника двох
чисел є наступним:
1) Вхід : цілі числа m і n.
2) Якщо m=n то GCD(m,n)=m=n. Якщо m≠n то
3) Якщо m>n то покласти m=m–n , інакше покласти n=n–m .Далі дивитись п.2.
Cуть алгоритму полягає у наступному. Нехай GCD(m,n) – функція, що
повертає найбільший спільний дільник чисел m і n. Якщо m = n, то GCD(m,n) =
m = n. Якщо m > n то GCD(m,n) = GCD(m-n, n), якщо n > m то GCD(m,n) =
GCD(m,n-m). Доведемо це.
Нехай GCD(m,n) = p, тоді m ділиться на p та n ділиться на p, відповідно,
m – n і n – m також діляться на p. Отже, GCD(m-n, n) і GCD (m, n-m) діляться на
GCD(m, n). Нехай q = GCD(m-n, n). Тоді маємо, що існують такі цілі t1 і t2, що
m-n = t1q, n = t2q тоді m = m – n + n = t1q + t2q = (t1+t2)q, GCD(m,n) ділиться на q.
З того, що p ділиться на q та q ділиться на p отримаємо, що p = q.
1) Алгоритм Евкліда обчислення найбільшого спільного дільника мовою
SIPL матиме наступну нотацію:
GCD =
begin
while M ≠ N do
if M > N then M:= M – N else N:= N - M
end
2) Побудуємо дерево синтаксичного виводу наведеної програми згідно
правил БНФ з таблиці 1.1, які задають мову SIPL.
За правилом NP1 БНФ мови SIPL бачимо, що нетермінал <програма>
представляється у вигляді <програма> ::= begin <оператор> end.
Отже, на 1-ому кроці дерево виводу буде мати такий вигляд:
<програма>
<оператор> end begin
16
На другому кроці розпишемо нетермінал <оператор>. Виходячи з коду
нашої програми, наш нетермінал <оператор> має саме такий вигляд:
<оператор> ::= while <умова> do <оператор> (правило NS4)
Отже, на другому кроці дерево виводу буде мати такий вид:
Далі розпишемо нетермінал <умова>.В нашому випадку <умова> ::=
<вираз> <вираз> (правило NB4)
Тут
кожен <вираз> представляється у вигляді <вираз> ::= <змінна> (правило NA2),
де в першому випадку <змінна> ::= M, а в другому <змінна> ::= N.
Отже, дерево синтаксичного виводу на наступному кроці має такий
вигляд:
<програма>
<оператор> end begin
while <умова> do <оператор>
<програма>
<оператор> end begin
while <умова> do <оператор>
<вираз> ≠ <вираз>
17
Тепер розпишемо другу частину оператора while.
Виходячи з коду програми, цей нетермінал має такий вигляд:
<оператор> ::= if <умова> then <оператор> else <оператор> (правило NS3) , де
умова оператора if: <умова> ::= <вираз> > <вираз> (правило NS7), де кожен з
виразів представлений змінною М та N відповідно.
Отже, дерево виводу на наступному кроці буде мати такий вигляд:
Далі залишилось розписати 2 оператора. Перший виконується тоді, коли
умова оператора if істинна, другий – коли хибна.
<програма>
oost
st <оператор> begin end
while <умова> do <оператор>
<вираз> ≠ <вираз> if <умова> then <оператор> else <оператор>
<змінна> <змінна> M
<вираз> > <вираз>
<змінна> <змінна> M N
M N
<програма>
<оператор> end begin
while <умова> do <оператор>
<вираз> ≠ <вираз>
<змінна> <змінна>
M N
18
Неважко переконатись, що в першому випадку на дереві виводу
<оператор> буде мати такий вид:
А в другому випадку:
Отже, остаточно дерево синтаксичного виводу для нашої програми буде
мати наступний вигляд:
if <умова> then <оператор> else <оператор>
<вираз> − <вираз> аk
<змінна> <вираз>
з> аз>
N
N
<змінна> <змінна>
M
if <умова> then <оператор> else <оператор> п<змінна>
<змінна> <змінна>:= <вираз>
<вираз> − <вираз>
M <змінна> <змінна>
0 <змінна>
N M
19
Для перевірки коректності побудови дерева треба обійти його зліва
направо, записуючи усі термінали (ті вирази, що записані без кутових дужок). В
результаті має бути отриманий вихідний текст програми на мові SIPL.
3) Композиційний семантичний терм визначимо згідно структури
програми за деревом виводу. Використовувати будемо правила його побудови в
мові SIPL, що задані в таблиці 1.3.
Sem_P(GCD) = sem_P(begin while M≠N do if M>N then M:=M-N else
N:=N-M end) = |застосовуємо правило NS_Prog | = sem_S(while M≠N do if M>N
then M:=M-N else N:=N-M) = | застосовуємо правило NS_Stm_Wh| =
WH(sem_B(M≠N), sem_S(if M>N then M:=M-N else N:=N-M)) = |для умови
циклу застосовуємо правило NS_B_neq, а для тіла NS_Stm_If| =
WH(S2(neq,sem_A(M), sem_A(N)), IF(sem_B(M>N), sem_S(M:=M-N),
sem_S(N:=N-M))) = |для кожної зі змінних M і N в умові циклу застосовуємо
правило NS_A_Var, для умови умовного оператора правило NS_B_gr, а для обох
його операторів NS_Stm_As | = WH(S2(neq, M=>, N=>)), IF(S
2(gr, sem_A(M),
<програма>
<оператор> begin end
while <умова> do <оператор>
<вираз> ≠ <вираз> if <умова> then <оператор> else <оператор>
<змінна> <змінна> <вираз> > <вираз> <змінна>:= <вираз> <змінна>:= <вираз>
<змінна> <змінна> M N
<вираз> − <вираз> <вираз> − <вираз>
M
M N N
N
<змінна> <змінна> <змінна> <змінна>
M M N
20
sem_A(N)),ASM
(sem_A(M-N)), ASN(sem_A(N-M)))) = |для кожної зі змінних M і
N в умові умовного оператора застосовуємо правило NS_A_Var, для інших двох
його операторів NS_A_Sub| = WH( S2(neq, M=>, N=>), IF(S
2(gr, M=>, N=>),
ASM
(S2(sub, sem_A(M), sem_A(N))), AS
N(S
2(sub, sem_A(N), sem_A(M))))) = |для
кожної зі змінних M і N в двічі застосовуємо правило NS_A_Var| = WH( S2(neq,
M=>, N=>), IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>,
M=>))))
4) Процес обчислення значення задається формулами таблиці 1.2. Щоб їх
застосовувати, треба робити конкретизацію загальних правил до конкретного
стану. Отже, завдання полягає в отриманні значення аплікації – застосування
функції, що задається нашим термом, до конкретного даного.
Отже, обчислимо Sem_P(GCD) ([M->15,N->9]) = WH( S2(neq, M=>, N=>),
IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>))))([M-
>15,N->9])
Правила для обчислення циклу AF_WH говорять про необхідність
поступового обчислення станів st0, st1, … і виконання тіла циклу (оператора fs)
до тих пір, поки умова fb є істинною. Обчислимо значення умови циклу S2(neq,
M=>, N=>) ([M->15,N->9]) = neq(15,9) = true, тому виконуємо тіло циклу із
композицією початкового циклу:
IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>)))
WH(…, …) ([M->15,N->9]) = WH(…, …) ( IF(S2(gr, M=>, N=>), AS
M(S
2(sub,
M=>, N=>)), ASN(S
2(sub, N=>, M=>))) ([M->15,N->9]) ) = | обчислимо значення
умови оператора IF: S2(gr, M=>, N=>) ([M->15,N->9]) = gr(15,9) = true, тому
виконуємо перший з двох операторів IF | = WH(…, …) ( ASM
(S2(sub, M=>, N=>))
([M->15,N->9]) ) = | обчислимо його, розписуючи присвоєння, віднімання та
розіменування: ASM
(S2(sub, M=>, N=>)) ([M->15,N->9]) = [M->15,N->9] [M-
>S2(sub, M=>, N=>) ([M->15,N->9])] = [M->15,N->9] [M->sub(15,9)] = [M-
>15,N->9][M->6] = [M->6,N->9]. Отже, ми виконали першу ітерацію циклу.
Далі за аналогією:
21
WH( S2(neq, M=>, N=>), IF(S
2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)),
ASN(S
2(sub, N=>, M=>)))) ( [M->6, N->9] ) = | S
2(neq, M=>, N=>) ([M->6,N->9]) =
neq(6,9) = true | = IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>,
M=>))) WH(…, …) ([M->6,N->9]) = WH(…, …) ( IF(S2(gr, M=>, N=>),
ASM
(S2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>))) ([M->6,N->9]) ) = | S
2(gr, M=>,
N=>) ([M->6,N->9]) = gr(6,9) = false | = WH(…, …) (ASN(S
2(sub, N=>, M=>)) ([M-
>6,N->9]) ) = | обчислимо ASN(S
2(sub, N=>, M=>)) ([M->6,N->9]) = [M->6,N->9]
[N->S2(sub, N=>, M=>) ([M->6,N->9])] = [M->6,N->9] [N-> sub(9,6)] = [M-
>6,N->9] [N->3] = [M->6,N->3] | = WH( S2(neq, M=>, N=>), IF(S
2(gr, M=>,
N=>), ASM
(S2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>)))) ( [M->6, N->3] ) = |
S2(neq, M=>, N=>) ([M->6,N->3]) = neq(6,3) = true | = IF(S
2(gr, M=>, N=>),
ASM
(S2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>))) WH(…, …) ( [M->6, N->3] )
= WH(…, …) ( IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>,
M=>))) ([M->6,N->3]) ) = | S2(gr, M=>, N=>) ([M->6,N->3]) = gr(6,3) = true | =
WH(…, …) ( ASM
(S2(sub, M=>, N=>)) ([M->6,N->3]) ) = | обчислимо AS
M(S
2(sub,
M=>, N=>)) ([M->6,N->3]) = [M->6,N->3] [M->S2(sub, M=>, N=>) ([M->6,N-
>3])] = [M->6,N->3] [M->sub(6,3)] = [M->6,N->3][M->3] = [M->3, N->3] | =
WH( S2(neq, M=>, N=>), IF(S
2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)),
ASN(S
2(sub, N=>, M=>)))) ( [M->3, N->3] ) = | Значення умови циклу S
2(neq,
M=>, N=>) ([M->3,N->3]) = neq(3,3) = false, тому зупиняємо обчислення із
поточним результатом | = [M->3, N->3].
Отже, Sem_P(GCD) ([M->15,N->9]) = [M->3, N->3].
Для зручності та скорочення можна перепозначати оператори і стани, що
виникають в процесі обчислення, наприклад: F0 := WH( S2(neq, M=>, N=>),
IF(S2(gr, M=>, N=>), AS
M(S
2(sub, M=>, N=>)), AS
N(S
2(sub, N=>, M=>)))) або d0 :=
[M->15,N->9]. Тоді умову можна переписати таким чином: знайти F0(d0), і
продовжувати обчислення. Оскільки треба ретельно слідкувати за даними та їх
зміною, тому краще записувати їх в окремій таблиці.
Завдання 1.2 Розробити програму, яка обчислює результат додавання res двох
невід‘ємних цілих чисел.
22
1) побудувати програму для розв’язку задачі на мові SIPL,
2) побудувати дерево синтаксичного виводу програми,
3) побудувати композиційний семантичний терм програми,
4) довести коректність програми.
Розв’язок.
1) Програма для розв’язку задачі:
Begin
rez:= а;
і:= 0;
while i < b do
begin
rez:= rez+1;
i:= i+1
end
End
2) Дерево синтаксичного виводу програми:
Першим кроком розпишемо нетермінал <програма>
<програма> ::= begin <оператор> end.
Отже, дерево синтаксичного виводу на 1-му кроці має такий вигляд:
Далі необхідно розписати мета змінну <оператор>. Цей не термінал
представляється у вигляді:
<оператор> ::= <змінна>:=<вираз> | <оператор> ; <оператор>|
if <умова> then <оператор> else <оператор> |
while <умова> do <оператор> |
begin <оператор> end | skip
<програма>
<оператор> end begin
23
Виходячи з коду програми на другому кроці дерево виводу буде мати
вигляд:
Розписуючи за правилом, наведеним вище, дерево виводу на третьому
кроці буде розписане таким чином
Далі нам доведеться розписати такі метазмінні, як <умова>, <змінна> та
<вираз>. Згідно синтаксису мови SIPL ці нетермінали представляються таким
чином:
<вираз> ::= <число> | <змінна> | <вираз> + <вираз> | <вираз> – <вираз> |
<вираз> * <вираз> | <вираз> ÷ <вираз> | (<вираз>)
<умова> ::= true | false | <вираз> < <вираз> | <вираз> <вираз> |
<вираз> = <вираз> | <вираз> <вираз> | <вираз> > <вираз> |
<вираз> <вираз> | <умова> <умова> | <умова> <умова> |
<умова> | (<умова>)
<оператор>
<end>
<програма>
; <оператор>
<begin>
<оператор> 3
<оператор>
<end>
<програма>
while
<умова>
do
<оператор>
; <оператор>
<оператор> <оператор>
<begin>
<оператор>
; 1 2
3
24
<оператор>
<end>
<програма>
<число>
0
<вираз>
b
<вираз>
<вираз>
<
<змінна>
<змінна>
while
<умова>
do
<оператор>
<змінна>
<оператор>
; <оператор>
:=
<вираз>
<вираз>
<вираз>
+
<число>
<змінна>
res
1
; <оператор>
:=
i
<вираз> <змінна>
<оператор> <оператор>
i
<begin> <end> <оператор>
<змінна>
:=
<вираз>
<вираз>
<вираз>
+
<число>
<змінна>
i
1
<begin>
<оператор>
;
<змінна>
a
:=
res
<змінна>
1 2
3
4 5
<оператор>
<end>
<програма>
<число>
0
<вираз>
b
<вираз>
<вираз>
<
<змінна>
while
<умова>
do
<оператор>
<змінна>
; <оператор>
:=
i
<вираз> <змінна>
<оператор> <оператор>
i
<begin> <end> <оператор>
<begin>
<оператор>
;
<змінна>
a
:=
res
<змінна>
1 2
3
Продовжуючи цей процес, отримаємо остаточний вигляд дерева:
25
3) Семантичний композиційний терм отримаємо без деталізації всіх кроків
перетворень:
sem_P(Program) = sem_S(res:= a) • sem_S(i:= 0) • sem_S(while i < b do begin
res:= res+1; i:= i+1 end) = ASres
(a=>) • ASi(0) • WH(S
2(less, sem_A(i), sem_A(b)),
sem_S(res:= res+1; i:= i+1)) =
=ASres
(a=>) • ASi(0) • WH(S
2(less, i=>, b=>), AS
res(S
2(add, res=>, 1)) • AS
i(S
2(add,
i=>, 1)))
4) Доведення коректності програми
Доведення часткової коректності:
Композиційні терми для відмічених в дереві виводу операторів (з 1 по 5)
будемо позначати відповідно f1, f2, f3, f4, та f5. Нехай d = [a→ a , b→b ], тоді:
sem_P(Program)(d) = f1 • f2 • f3(d) = f3(f2 (f1(d))) = f3(f2 (d [res→a(d)])) = f3(f2 (d
[res→ a ])) = f3(f2 (d1)) = | d1 = [a→ a , b→b , res→ a ] | = f3(d1 [i→0]) =
= f3(d2) = | d2 = [a→ a , b→b , res→ a , i→0] | =
=
false )b(0 ,d
true )b(0 )b less(0, ))d(b ),d(i (less,S ))(db ,i (less,S ))),d(f(f(f
2
22
2
2
2
2453
Позначимо G = WH(S2(less, i=>, b=>), AS
res(S
2(add, res=>, 1)) •AS
i(S
2(add, i=>,
1))).
Нехай маємо dw = [a→ a , b→b , res→ a , i→0], тоді висунемо гіпотезу:
Гіпотеза: при виконанні n кроків тіла циклу G(dw) = [a→ a , b→b , res→ a +n,
i→n]
База. 1 крок: G(dw) = |(i < b)=true |=
= ASres
( S2(add, res=>, 1)) •AS
i(S
2(add, i=>, 1)) (dw) = AS
res( S
2(add, a , 1))
•ASi(S
2(add, 0, 1)) = AS
res( a +1) •AS
i(1) = dw [res→ a +1, i→1] = [a→ a , b→b ,
res→ a +1, i→1]
Припущення: припустимо, що виконано t-1 кроків тіла циклу, i дані мають вид
[a→ a , b→b , res→ a +t-1, i→t-1]. Покажемо, як зміняться дані після виконання
ще одного кроку циклу:
G([a→ a , b→b , res→ a +t-1, i→t-1]) = [a→ a , b→b , res→ a +t, i→t]. Оскільки тіло
циклу виконується, то (less, i=>, b=>) = true, тобто (i < b) = true, i < b, i = t-1, t-1
< b i ASres
(S2(add, res=>, 1)) •AS
i(S
2(add, i=>, 1))( [a→ a , b→b , res→ a +t-1, i→t-
1]) = [a→ a , b→b , res→ a +t-1, i→t-1] [ res→ a +t; i→t] = [a→ a , b→b , res→ a +t,
i→t].
Отже, гіпотезу доведено.
26
Таким чином, ми показали часткову коректність програми при
правильних вхідних даних (тобто a>0 і b>0).
Обґрунтування повної коректності:
Покажемо, що при правильних вхідних даних програма завершується
(зупиняється). Фактично, треба показати завершуваність циклу G, оскільки інші
оператори не впливають на завершення програми. З часткової коректності
видно, що за правильних вхідних даних перед виконанням циклу дані мають
вигляд [a→ a , b→b , res→ a , i→0], де b > 0 і a > 0. Розглянемо послідовність
значень змінної і: після кожного виконання циклу i збільшується на 1 та і < b.
Отже, в силу скінченності b, якщо програма входить у цикл, то він завжди
завершується. При інших варіантах правильних вхідних даних програма не
заходить в цикл, а послідовно виконує прості оператори, тому програма завжди
зупиняється в силу скінченності кількості операторів.
Завдання 1.3. За допомогою операцій віднімання і додавання одиниці розробити
програму, яка обчислює результат множення цілих чисел A та B. Виконати:
1) побудувати програму для розв’язку задачі на мові SIPL,
2) побудувати композиційний семантичний терм програми,
3) довести коректність програми.
Розв’язок.
1) Програма для розв`язку задачі:
Begin
R:=0;
if (N<0) then begin A:=-M;B:=-N end
else begin A:=M;B:=N end;
while (B>0) begin R:=R+A;B:=B-1 end
End
2) Семантичний композиційний терм. Для спрощення нотації ми будемо писати
семантичні значення чисел-констант без знаку рисочки над власне числом
(іменем константи) – розрізнити чи є число синтаксичним позначенням, чи
семантичним значенням завжди можна буде по контексту.
sem_P(Program) = sem_P(Begin R:=0; if (N<0) then begin A:=-M;B:=-N end else
begin A:=M;B:=N end ; while (B>0) begin R:=R+A;B:=B-1 end End)=
= sem_S( R:=0; if (N<0) ten begin A:=-M;B:=-N end else begin A:=M;B:=N end;
while (B>0) begin R:=R+A;B:=B-1 end) =
27
= sem_S(R:= 0)•sem_S(if (N<0) then begin A:=-M;B:=-N end else begin
A:=M;B:=N end; while (B>0) begin R:=R+A;B:=B-1 end) =
= ASR(sem_A(0))•sem_S(if (N<0) then begin A:=-M;B:=-N end else begin
A:=M;B:=N end)•sem_S(while (B>0) begin R:=R+A;B:=B-1 end) =
= ASR(0)• IF(sem_B(N<0), sem_S( begin A:=-M;B:=-N end ), sem_S( begin
A:=M;B:=N end))•WH(sem_B(B > 0), sem_S(begin R:=R+A;B:=B-1 end))=
= ASR(0)• IF(S
2 (less, sem_A(N), sem_A(0)), sem_S( A:=-M;B:=-N ), sem_S(
A:=M;B:=N)) •WH(S2 (gr, sem_A (B), sem_A(0)), sem_S( R:=R+A ; B:=B-1)) =
= ASR(0)• IF(S
2 (less,N=>,0), sem_S( A:=-M) • sem_S (B:=-N), sem_S( A:=M) •
sem_S ( B:=N)) •WH(S2 (gr,B=>,0), sem_S( R:=R+A) • sem_S (B:=B-1)) =
= ASR(0)• IF(S
2 (less,N=>,0), AS
A(sem_A(0-M)) • AS
B(sem_A(0-N)),
ASA(sem_A(M)) • AS
B(sem_A(N)) •WH(S
2 (gr,B=>,0), sem_S( R:=R+A) • sem_S
(B:=B-1)) =
= ASR(0)• IF(S
2 (less,N=>,0), AS
A(S
2 (sub, sem_A (0), sem_A (M))) • AS
B(S
2 (sub,
sem_A (0), sem_A (N))), ASA(M=>) • AS
B(N=>)) •WH(S
2 (gr,B=>,0), AS
R
(sem_A(R+A)) • ASB(sem_A(B-1)) =
= ASR(0)• IF(S
2 (less,N=>,0), AS
A(S
2 (sub, 0,M)) • AS
B(S
2 (sub, 0,N)), AS
A(M=>) •
ASB(N=>)) •WH(S
2 (gr,B=>,0), AS
R (S
2 (add, sem_A(R), sem_A(A))) • AS
B (S
2 (sub,
sem_A(B), sem_A(1))))=
= ASR(0)• IF(S
2 (less,N=>,0), AS
A(S
2 (sub, 0,M)) • AS
B(S
2 (sub, 0,N)), AS
A(M=>) •
ASB(N=>)) •WH(S
2 (gr,B=>,0), AS
R (S
2 (add,R=>,A=>)) • AS
B (S
2 (sub,B=>, 1)))
3) Доведення часткової коректності.
Нехай F= ASR(0)• IF(S
2 (less,N=>,0), AS
A(S
2 (sub, 0,M)) • AS
B(S
2 (sub, 0,N)),
ASA(M=>) • AS
B(N=>)) •WH(S
2 (gr,B=>,0), AS
R (S
2 (add,R=>,A=>)) • AS
B (S
2
(sub,B=>, 1)))
F1= ASR(0);
F2= IF(S2 (less,N=>,0), AS
A(S
2 (sub, 0,M)) • AS
B(S
2 (sub, 0,N)), AS
A(M=>) •
ASB(N=>))
F3= WH(S2 (gr,B=>,0), AS
R (S
2 (add,R=>,A=>)) • AS
B (S
2 (sub,B=>, 1)))
Тоді F= F1• F2• F3
Нехай маємо дані d=[M->m, N->n]. Доведемо, що на них програма
працює правильно, тобто що при завершенні обчислень отримується бажаний
результат:
F(d) = (F1• F2• F3)(d) = F3(F2(F1(d)))
F1(d)= ASR(0)(d)= AS
R(0)([ M->m, N->n])=[ M->m, N->n] ▼[R->0]= [ M->m, N-
>n, R->0]= d1
28
F(d)= F3(F2(F1(d)) )= F3(F2(d1)), де d1=[M->m, N->n, R->0]
F2(d1)= IF(S2 (less,N=>,0), AS
A(S
2 (sub, 0,M)) • AS
B(S
2 (sub, 0,N)), AS
A(M=>) •
ASB(N=>))( d1)=
2 2 2
2
( ( ,0, ) ( ( ,0, ))), ( , ,0)( 1)
( ) ( ), ( , ,0)
A B
A B
AS S sub M AS S sub N S less N trued
AS M AS N S less N false
=
2 2( ( ,0, ) ( ( ,0, ))), 0([ , , 0])
( ) ( ), 0
A B
A B
AS S sub M AS S sub N NM m N n R
AS M AS N N
=
2 2( ( ,0, ) ( ( ,0, )))([ , , 0]), 0
( ) ( )([ , , 0]), 0
A B
A B
AS S sub M AS S sub N M m N n R n
AS M AS N M m N n R n
=
[ , , 0] [ , ], 0
[ , , 0] [ , ], 0
M m N n R A m B n n
M m N n R A m B n n
=
[ , , 0, , ] 2, 0
[ , , 0, , ] 3, 0
M m N n R A m B n d n
M m N n R A m B n d n
2, 02( 1)
3, 0
d nF d
d n
2 [ , , 0, , ]
3 [ , , 0, , ]
d M m N n R A m B n
d M m N n R A m B n
F= F3(F2(d1)) = 3( 2), 0
3( 2( 1))3( 3), 0
F d nF F d
F d n
За допомогою методу математичної індукції за кількістю кроків циклу
знайдемо F3(F2(d1)) , враховуючи, що n≥ 0,а потім n<0. Розглянемо спочатку
випадок n≥ 0.
Гіпотеза: при виконанні n кроків тіла циклу F3(F2(d2)) =
[M→m,N→n,R→m*n,A→m,B→0]
База: 0 крок: b=0 =>умова циклу не виконується ,маємо таке результуюче дане:
[M→m, N→n, R→0, A→m, B→0] => R= m*n =0.Гіпотеза справджується.
Припущення: припустимо, що виконано t-1 кроків тіла циклу, тоді дані мають
вигляд d*= [M→m, N→n, R→m*(t-1), A→m, B→n-t+1]. Покажемо, як зміняться
дані після виконання ще одного кроку циклу :
F3 (d*)= F3 ([M→m, N→n, R→m*(t-1), A→m, B→n-t+1]) = [M→m, N→n, R→m*t,
A→m, B→n-t]. Оскільки тіло циклу виконується, то S2
(gr,B=>,0) = true, тобто
b>0, n-t>0 => n>t, n>n-t+1 і виконується тіло циклу ASR (S
2 (add,R=>,A=>)) •
ASB (S
2 (sub,B=>, 1)) ([M→m, N→n, R→m*(t-1), A→m, B→n-t+1]) = [M→m,
N→n, R→m*(t-1), A→m, B→n-t+1] [R→ m*(t-1)+m; B→ n-t] = [M→m, N→n,
R→m*t, A→m, B→n-t].При кількості кроків циклу t=n отримаємо результат
[M→m N→,n, R→m*n, A→m, B→0].
29
Умова циклу S2 (gr,B=>,0) = false.
Отже, гіпотезу доведено.
Тепер аналогічно розглянемо випадок, коли n<0.
Гіпотеза: при виконанні n кроків тіла циклу F3(F2(d3)) =[ M→m, N→n, R→ (-
m)*(-n), A→ -m, B→0]
База: 0 крок: b=0 => умова циклу не виконується ,маємо таке результуюче
дане: [M→m, N→n, R→0, A→ -m, B→0] => R= (-m)*(-n) = 0. Гіпотеза
справджується.
Припущення: припустимо, що виконано t-1 кроків тіла циклу, тоді дані мають
вид d**= [M→m, N→n, R→-m*(t-1), A→ -m, B→ -n-t+1]. Покажемо, як
зміняться дані після виконання ще одного кроку циклу :
F3 (d**)= F3 ([M→m, N→n, R→ (-m)*(t-1), A→ -m, B→ -n-t+1]) = [M→m, N→n,
R→ (-m)*t, A→ -m, B→ -n-t]. Оскільки тіло циклу виконується, то S2
(gr,B=>,0)
= true, тобто b>0, n-t>0 => n>t, n>n-t+1 і виконується тіло циклу ASR (S
2
(add,R=>,A=>)) • ASB (S
2 (sub,B=>, 1)) ([M→m, N→n, R→ (-m)*(t-1), A→ -m,
B→ -n-t+1]) = [M→m, N→n, R→ (-m)*(t-1), A→ -m, B→ -n-t+1] [R→ (-m)*(t-1)-
m; B→ -n-t] = [M→m, N→n, R→ (-m)*t, A→ -m, B→ -n-t]. При кількості кроків
циклу t=-n отримаємо результат [M→m, N→n, R→ (-m)*(-n), A→ -m, B→0].
Умова циклу S2 (gr,B=>,0) = false.
Отже, гіпотезу доведено.
Обґрунтування повної коректності.
Покажемо, що при правильних вхідних даних програма завершується
(зупиняється). Фактично, треба показати завершеність циклу F3 , оскільки інші
оператори не впливають на завершення програми. З часткової коректності
видно, що за правильних вхідних даних перед виконанням циклу дані мають
вигляд [M→m, N→n, R→0, A→m, B→n]. Після кожного виконання циклу R
збільшується на m і b зменшується на 1. Отже,отримаємо спадну послідовність
b і в силу її скінченності, якщо програма входить у цикл, то він завжди
завершується. При інших варіантах правильних вхідних даних програма не
заходить в цикл, а послідовно виконує прості оператори, тому програма завжди
зупиняється в силу скінченності кількості операторів.
Завдання 1.4. За допомогою операцій віднімання і додавання розробити
програму, яка обчислює результат ділення rez невід’ємного числа a на додатне
число b та остачу від цього ділення – ost. Виконати:
1) побудувати програму для розв’язку задачі на мові SIPL,
2) побудувати дерево синтаксичного виводу програми,
30
3) побудувати композиційний семантичний терм програми,
4) довести коректність програми.
Розв’язок.
1) Програма для розв’язку задачі:
Begin
rez:= 0;
k:= 0;
if (a = 0) then skip else
if (a < b) then skip else
while k <= a – b do
begin
rez:= rez+1;
k:= k+b
end;
ost:= a – k
End
2) Дерево синтаксичного виводу програми:
31
3) Семантичний композиційний терм:
sem_P(Program) = sem_P(Begin rez:= 0; k:= 0; if (a = 0) then skip else if (a < b)
then skip else while k <= a – b do begin rez:= rez+1; k:= k+b end; ost:=a - k End)=
= sem_S(rez:= 0; k:= 0; if (a = 0) then skip else if (a < b) then skip else while k <=
a – b do begin rez:= rez+1; k:= k+b end; ost:=a - k) =
= sem_S(rez:= 0)•sem_S(k:= 0; if (a = 0) then skip else if (a < b) then skip else
while k <= a – b do begin rez:= rez+1; k:= k+b end; ost:=a - k)=
= sem_S(rez:= 0)•sem_S(k:= 0; if (a = 0) then skip else if (a < b) then skip else
while k <= a – b do begin rez:= rez+1; k:= k+b end)•sem_S(ost:=a - k) =
:=
<begin> <оператор>
<end>
<оператор> <оператор> ;
<змінна>
<програма>
<вираз> <оператор> ; <оператор>
rez
<число>
0
if <умова> then
<змінна> := <вираз>
<оператор> else
<оператор>
<вираз> <вираз> = skip
<змінна> <число>
a 0
0
if <умова> then <оператор> else
<оператор>
<вираз>
<вираз>
<
<змінна>
<змінна>
b
a
skip
while
<умова>
do
<оператор>
<вираз>
<вираз>
<
<змінна>
<вираз>
<вираз>
-
<оператор>
; <оператор>
<вираз>
:=
<вираз>
<вираз>
:=
<вираз>
<вираз>
<вираз>
+
<вираз>
<вираз>
+
<число>
<змінна>
<змінна>
<змінна>
rez
1
k
b
k
<змінна>
<змінна>
rez
<змінна>
<змінна>
<змінна>
k
a
b
;
<оператор>
<змінна> := <вираз>
k 0
ost <вираз> - <вираз>
<змінна> <змінна>
k a
32
= ASrez
(sem_A(0))•sem_S(k:= 0)•sem_S(if (a = 0) then skip else if (a < b) then skip
else while k <= a – b do begin rez:= rez+1; k:= k+b end) •ASost
(sem_A(a-k))=
= ASrez
(0)• ASk(sem_A(0))•IF(sem_B(a = 0), sem_S(skip), sem_S(if (a < b) then
skip else while k <= a – b do begin rez:= rez+1; k:= k+b end))•ASost
(S2(sub,
sem_A(a), sem_A(k)))=
= ASrez
(0)• ASk(0)• IF(sem_B(a = 0), sem_S(skip), sem_S(if (a < b) then skip else
while k <= a – b do begin rez:= rez+1; k:= k+b end)) •ASost
(S2(sub, a=>, k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, sem_A(a), sem_A(0)), id, IF(sem_B(a<b),
sem_S(skip), sem_S(while k <= a – b do begin rez:= rez+1; k:= k+b end)))
•ASost
(S2(sub, a=>, k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, sem_A(a), sem_A(b)), id,
WH(sem_B(k <= a - b), sem_S(begin rez:= rez+1; k:= k+b end)))) •ASost
(S2(sub,
a=>, k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less,
sem_A(k), sem_A(a-b)), sem_S(rez:= rez+1; k:= k+b)))) •ASost
(S2(sub, a=>, k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>,
S2(sub, a=>, b=>)), sem_S(rez:= rez+1)•sem_S(k:= k+b)))) •AS
ost(S
2(sub, a=>,
k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>,
S2(sub, a=>, b=>)), AS
rez(sem_A(rez+1))•AS
k(sem_A(k+b))))) •AS
ost(S
2(sub, a=>,
k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>,
S2(sub, a=>, b=>)), AS
rez((S
2(add, sem_A(rez), sem_A(1))))•AS
k(S
2(add, sem_A(k),
sem_A(b)))))) •ASost
(S2(sub, a=>, k=>))=
= ASrez
(0)• ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>,
S2(sub, a=>, b=>)), AS
rez((S
2(add, rez=>, 1)))•AS
k(S
2(add, k=>, b=>)))))
•ASost
(S2(sub, a=>, k=>))
4) Доведення часткової коректності:
Для скорочення запису будемо записувати дані у впорядкованому виді,
тобто [a,b,rez,k,ost], замість [a→ a , b→b , rez→ rez , k→ k , ost → ost ].
Позначимо
F = ASk(0)• IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>, S
2(sub,
a=>, b=>)), ASrez
((S2(add, rez=>, 1)))•AS
k(S
2(add, k=>, b=>))))) •AS
ost(S
2(sub, a=>,
k=>)),
тоді маємо
sem_S([a,b,rez,k,ost])=F(ASrez
(0))([a,b,rez,k,ost])=F([a,b,rez,k,ost][rez→0])=F([a,b
,0,k,ost])
33
Позначимо
F1 = IF(S2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>, S
2(sub, a=>,
b=>)), ASrez
((S2(add, rez=>, 1)))•AS
k(S
2(add, k=>, b=>))))) •AS
ost(S
2(sub, a=>,
k=>)).
Маємо
F = ASk(0)•F1
F([a,b,0,k,ost]) =
F1(ASk(0))([a,b,0,k,ost])=F1([a,b,0,k,ost]▼[k→0])= F1([a,b,0,0,ost]).
F1([a,b,0,0,ost]) =
IF(S2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less, k=>, S
2(sub, a=>, b=>)),
ASrez
((S2(add, rez=>, 1))) •AS
k(S
2(add, k=>, b=>))))• AS
ost(S
2(sub, a=>, k=>))
([a,b,0,0,ost])=
ASost
(S2(sub, a=>, k=>))( IF(S
2(eq, a=>, 0), id, IF(S
2(less, a=>, b=>), id, WH(S
2(less,
k=>, S2(sub, a=>, b=>)), AS
rez((S
2(add, rez=>, 1))) •AS
k(S
2(add, k=>,
b=>)))))([a,b,0,0,ost])=
0aost]),b,0,0,)))))([a,b ,k (S2(add,AS 1))) ,rez (add,((SAS
)),b ,a (sub,S ,k (less, WH(Sid, ),b ,a (less,))(IF(Sk ,a (sub,(SAS
0a ost]),b,0,0,))([a,k ,a (sub,(SAS
k2rez
2222ost
2ost
=
b a і 0aost]),b,0,0,))))([a,b ,k (S2(add,AS 1))) ,rez (add,((SAS
)),b ,a (sub,S ,k (less,))(WH(Sk ,a (sub,(SAS
ba і 0aost]),b,0,0,))([a,k ,a (sub,(SAS
0]),,0,0,,))([()((
k2rez
222ost
2ost
aostbakaAS ost
=
b a і 0aost]),b,0,0,))))([a,b ,k (S2(add,AS 1))) ,rez (add,((SAS
)),b ,a (sub,S ,k (less,))(WH(Sk ,a (sub,(SAS
b) a і 0(a або 0a],[],0,0,,[
k2rez
222ost
kaostostba
.
Позначимо
G = WH(S2(less, k=>, S
2(sub, a=>, b=>)), AS
rez((S
2(add, rez=>, 1))) •AS
k(S
2(add, k=>, b=>)))))
Маємо F1([a,b,0,0,ost]) =
b a і 0aost]),b,0,0,))(G)([a,k ,a (sub,(SAS
b) a і 0(a або 0a],,0,0,,[2ost
aba
За допомогою методу математичної індукції за кількістю кроків циклу
знайдемо, G([a,b,0,0,ost]), враховуючи, що a ≥ b і a ≠ 0 .
Гіпотеза: при виконанні n кроків тіла циклу G([a, b, 0, 0, ost]) = ],,,,[ ostnbnba
База. 1 крок: G([a,b,0,0,ost]) = |(k < a-b)=true => k < a-b => 0 < a-b => b<a|=
= ASrez
( (S2(add, rez=>, 1))) •AS
k(S
2(add, k=>, b=>)))) ([a,b,0,0,ost])=AS
rez(
(S2(add, 0, 1))) •AS
k(S
2(add, 0, b))= AS
rez(1) •AS
k(b)=[a,b,0,0,ost] [rez→1, k→b] =
[a,b,1,b,ost]
34
Припущення: припустимо, що виконано t-1 кроків тіла циклу, тоді дані мають
вид [a,b,t-1,b*(t-1),ost]. Покажемо, як зміняться дані після виконання ще одного
кроку циклу :
G([a,b,t-1,b*(t-1),ost]) = [a,b,t,b*t,ost]. Оскільки тіло циклу виконується, то (less,
k=>, S2(sub, a=>, b=>)) = true, тобто (k < a-b) = true, k < a-b, k = t-1, t-1 < a-b
виконується тіло циклу ASrez
(S2(add, rez=>, 1)) •AS
k(S
2(add, k=>, b=>))([a,b,t-
1,b*(t-1),ost]) = [a,b,t-1,b*(t-1),ost] [rez→t; k→b*t] = [a,b,t,b*t,ost].
Отже, гіпотезу доведено.
Нехай G(d) зупиняється на даному : [a,b,n,b*n,ost], це значить, що умова циклу
приймає значення false. Маємо : S2(less, b*n=>, (a=>)-(b=>)) = false => (b*n < a-
b) = false => b*n≥a-b, враховуючи, що умова на n-1 кроі виконання циклу була
істиною, маємо : b*n ≥a – b≥ b*(n-1) => b*(n+1)≥a≥b*n. Отже n – ціла частина
від ділення a на b.
Маємо F1([a,b,0,0,ost]) =
=
b a і 0aost]),b,0,0,))(G)([a,k ,a (sub,(SAS
b) a і 0(a або 0a],,0,0,,[2ost
aba=
=
b a і 0a]),],/[*],/[,,))([,,((AS
b) a і 0(a або 0a],,0,0,,[2ost ostbabbabakasubS
aba=
=
b a і 0a]),],/[*],/[,,))([,,((AS
b) a і 0(a або 0a],,0,0,,[2ost ostbabbabakasubS
aba=
=
b a і 0a)],()([]],,[*],/[,,[
b) a і 0(a або 0a],,0,0,,[
kaostostbabbaba
aba=
=
b a і 0a]],/[*],/[*],/[,,[
b) a і 0(a або 0a],,0,0,,[
babababbaba
aba
Отже, ми показали часткову коректність програми при правильних
вхідних даних (тобто a≥0 і b>0).
Обґрунтування повної коректності.
Покажемо, що при правильних вхідних даних програма завершується
(зупиняється). Фактично, треба показати завершеність циклу G, оскільки інші
оператори не впливають на завершення програми. З часткової коректності
видно, що за правильних вхідних даних перед виконанням циклу дані мають
вигляд [a,b,rez=0,k=0,ost], де a ≥ b, b > 0 і a ≠ 0. Після кожного виконання
циклу k збільшується на b і k+b < a. Отже, в силу скінченності a і b, якщо
програма входить у цикл, то він завжди завершується. При інших варіантах
правильних вхідних даних програма не заходить в цикл, а послідовно виконує
прості оператори, тому програма завжди зупиняється в силу скінченності
кількості операторів.
35
Завдання 1.5. Розробити програму, яка обчислює значення N! за вхідним цілим
N з використанням одного циклу. Виконати:
1) побудувати програму для розв’язку задачі на мові SIPL,
2) побудувати дерево синтаксичного виводу програми,
3) побудувати композиційний семантичний терм програми,
4) довести коректність програми.
Розв’язок.
1) Програма для розв’язку задачі:
Begin
If N<0 then R:=0 else
begin
R:=1; M:=N;
while M>1 do begin R:=R∙M; M:=M–1 end
end
End
2) Дерево синтаксичного виводу програми:
36
3) Семантичний композиційний терм:
sem_P(Program) = sem_P(Begin If N<0 then R:=0 begin R:=1; M:=N; while M>1
do begin R:=R∙M; M:=M-1 end end end) = sem_S(If N<0 then R:=0 begin R:=1;
M:=N; while M>1 do begin R:=R∙M; M:=M-1 end end) = IF(sem_B(N<0),
sem_S(R:=0), sem_S(R:=1; M:=N while M>1 do begin R:=R∙M; M:=M-1 end) ) =
begin <оператор>
end
<програма>
if <умова> then <оператор> else
<оператор>
<вираз> <вираз> <
<змінна>
N 0
0
<константа>
<змінна> <вираз> :=
R
0
0
<константа>
begin <оператор> end
<оператор>
<оператор> <оператор> ;
<вираз> <змінна>
R
1
0
<константа> M
N
0
<змінна>
:= <вираз> <змінна> :=
begin <оператор> end
<оператор> <оператор> ;
<вираз> <змінна>
R
:= <вираз> <змінна>
M
:=
<вираз> <вираз> ∙ <вираз> <вираз> –
<змінна> <змінна> <константа> <змінна>
M 1
0
R M
0
; <оператор>
while <умова> do <оператор>
<вираз> <вираз> >
<змінна>
M 1
0
<константа>
37
IF(sem_B(N<0), sem_S(R:=0), sem_S(R:=1;M:=N) ◦ WH(sem_B(M>1),
sem_S(R:=R∙M; M:=M-1) ) ) = IF(sem_B(N<0), sem_S(R:=0), sem_S(R:=1) ◦
sem_S(M:=N) ◦ WH(sem_B(M>1), sem_S(R:=R∙M) ◦ sem_S( M:=M-1) ) ) =
IF(S2 (less, sem_A(N), sem_A(0)), AS
R(sem_A(0)), AS
R(sem_A(1)) ◦
ASM
(sem_A(N)) ◦ WH(S2 (gr, sem_A(M), sem_A(1)), AS
R(sem_A(R∙M)) ◦
ASM
(sem_A(M-1)) ) ) = IF(S2 (less, N=>,0), AS
R(0), AS
R(1) ◦ AS
M(N=>) ◦ WH(S
2
(gr, M=>, 1), ASR(S
2(mult, sem_A(R), sem_A(M))) ◦ AS
M(S
2(sub, sem_A(M),
sem_A(1))) ) ) = IF(S2 (less, N=>,0), AS
R(0), AS
R(1) ◦ AS
M(N=>) ◦ WH(S
2 (gr, M=>,
1), ASR(S
2(mult, R=>,M=>)) ◦ AS
M(S
2(sub, M=>,1)) ) )
4) Доведення часткової коректності.
Нехай маємо дані d=[N->n]. Доведемо, що на них програма працює
правильно, тобто що при завершенні обчислень отримується очікуваний
результат:
F= IF(S2 (less, N=>,0), AS
R(0), AS
R(1) ◦ AS
M(N=>) ◦ WH(S
2 (gr, M=>, 1),
ASR(S
2(mult, R=>,M=>)) ◦ AS
M(S
2(sub, M=>,1)) ) )
F1= ASR(1);
F2= ASM
(N=>);
F3= WH(S2 (gr, M=>, 1), AS
R(S
2(mult, R=>,M=>)) ◦ AS
M(S
2(sub, M=>,1)));
F4= ASR(0);
F5= ASR(S
2(mult, R=>,M=>));
F6= ASM
(S2(sub, M=>,1));
1) нехай n<0
Sem_p(F)(d)=
truenless
ddNlessS
dNlessS
)0,(
))(0),(,(
))(0,,(
2
2
=F4(d)=F4(d▼[R->0]) =
= F4(d▼[R->0]) = dr = [N->n, R->0] ;
факторіал для від’ємних чисел не існує і результат дорівнює нулю (помилка у
ввідних даних).
2) нехай n≥0
Sem_p(F)(d)=
falsenless
ddNlessS
dNlessS
)0,(
))(0),(,(
))(0,,(
2
2
= F3(F2(F1(d))) =
= F3(F2(d▼[R->1]) = F3(F2([N->n,R->1]) = 1]R- n,[N-d1 =
= F3(d1▼ [M->N=>(d1)]) = F3(d1▼ [M->n]) = 1]R-n,N-n,[M-d2 =
= F3(d2);
Далі за допомогою методу математичної індукції за кількістю кроків
циклу знайдемо F3(d2), враховуючи, що n≥ 1, а потім n<1. Нехай спочатку n≥ 1.
38
Гіпотеза: при виконанні n кроків тіла циклу F3(d2)=[ {1,0}mM-,m!•pR-n,N- ]
База: Нульовий крок: При m=0 умова циклу не виконується, маємо таке
результуюче дане: [ 0M-p,R-n,N- ] => R= p*m! = p. Гіпотеза справджується.
Припущення: припустимо, що виконано t-1 кроків тіла циклу, тоді дані мають
вид dw= [N->n,R->p,M->t-1]. Покажемо, як зміняться дані після виконання ще
одного кроку циклу:
F3 (dw)= F3 ([N->n,R->p,M->t]) =
0,
0,)1,(
))(1),(,(
))(1,,(
2
2
tfalse
ttruetgr
dwdwMgrS
dwMgrS
;
При t=0:
F3 (dw) = F3 ([N->n,R->p,M->0])= [N->n,R->p,M->0];
dr = [N->n,R->p∙0!,M->0] = dw; гіпотеза справджується.
При t>0:
F3 (dw) = F5 ◦ F6 ◦ F3(dw) = ]1,,[ mMmpRnNdd = F3(dd) =
= tddM )( = }]1,0{,)!1(,[ mMmmpRnNdq = dq = dr;
dr - dw
dq - dd
=> F3(dw) = {0,1}]mM,n!•1Rn,[N
Отже, гіпотезу доведено.
Обчислення завершене. Результат поміщується у вихідному даному R.
Маємо R->1∙n!. Оскільки у вхідному даному значення змінної N було n, то
дійсно отримали обчислення факторіалу.
Обґрунтування повної коректності.
Покажемо, що при правильних вхідних даних програма завершується
(зупиняється). Фактично, треба показати завершуваність циклу F3 , оскільки
інші оператори не впливають на завершення програми. З часткової коректності
видно, що за правильних вхідних даних перед виконанням циклу дані мають
вигляд [N->n,R->1,M->m] (де n>0). Після кожного виконання циклу R
збільшується у m разів і m зменшується на 1. Отже,отримаємо спадну
послідовність m і в силу її скінченності, якщо програма входить у цикл, то він
завжди завершується. При інших варіантах правильних вхідних даних програма
не заходить в цикл, а послідовно виконує прості оператори, тому програма
завжди зупиняється в силу скінченності кількості операторів.
39
Завдання 1.6. Розробити програму, яка за допомогою операції додавання
обчислює результат піднесення значення a до степеня b. Виконати:
1) побудувати програму для розв’язання задачі на мові SIPL,
2) побудувати дерево синтаксичного виводу програми,
3) побудувати композиційний семантичний терм програми,
4) довести коректність програми.
Розв’язок.
1) Програма на мові SIPL, яка обчислює a^b через додавання
begin
i:=1;
r:=1;
while i ≤ b do
begin
k:=1;
p:=r;
while a ≠ k do
begin
r:=r+p;
k:=k+1
end;
i:=i+1
end
end
40
2) Дерево синтаксичного виводу програми :
3) Семантичний терм програми:
Sem_P(a^b) = Sem_P(begin i:=1;r:=1;while i≤b do begin k:=1;p:=r; while a≠ k do
begin r:=r+p; k:=k+1 end; i:=i+1 end end) =
Sem_S(i:=1;r:=1;while i≤b do begin k:=1;p:=r; while a≠ k do begin r:=r+p; k:=k+1
end; i:=i+1 end) =
Sem_S(i:=1) ◦ Sem_S(r:=1) ◦ Sem_S(while i≤b do begin k:=1;p:=r; while a≠ k do
begin r:=r+p; k:=k+1 end; i:=i+1 end) =
ASi(Sem_A(1)) ◦ AS
r(Sem_A(1)) ◦ WH(Sem_B(i≤b),Sem_S(begin k:=1;p:=r; while
a≠ k do begin r:=r+p; k:=k+1 end; i:=i+1 end))=
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,Sem_A(i), Sem_A(b)),Sem_S(k:=1;p:=r; while a≠ k do
begin r:=r+p; k:=k+1 end; i:=i+1)) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>),Sem_S(k:=1) ◦ Sem_S(p:=r) ◦ Sem_S(while
a≠ k do begin r:=r+p; k:=k+1 end; i:=i+1)) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(Sem_A(1)) ◦ AS
p(Sem_A(r)) ◦
WH(Sem_B (a≠ k) , Sem_S(begin r:=r+p; k:=k+1 end)) ◦ Sem_S( i:=i+1) =
41
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(1) ◦ AS
p(r=>) ◦ WH(S
2(neq, Sem_A
(a), Sem_A (k)) , Sem_S(r:=r+p; k:=k+1)) ◦ ASi(Sem_A(i+1)) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(1) ◦ AS
p(r=>) ◦ WH(S
2(neq, a=>, k=>) ,
Sem_S(r:=r+p) ◦ Sem_S(k:=k+1)) ◦ ASi(S
2 (add, Sem_A(i), Sem_A(1))) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(1) ◦ AS
p(r=>) ◦ WH(S
2(neq, a=>, k=>) ,
ASr(Sem_A(r+p)) ◦ AS
k (Sem_A(k+1))) ◦ AS
i(S
2 (add, i=>, 1)) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(1) ◦ AS
p(r=>) ◦ WH(S
2(neq, a=>, k=>) ,
ASr(S
2(add, Sem_A(r), Sem_A(p))) ◦ AS
k (S
2(add, Sem_A(k), Sem_A(1))) ) ◦ AS
i(S
2
(add, i=>, 1)) =
ASi(1) ◦ AS
r(1) ◦ WH(S
2(leq,i=>, b=>), AS
k(1) ◦ AS
p(r=>) ◦ WH(S
2(neq, a=>, k=>) ,
ASr(S
2(add, r=>, p=>)) ◦ AS
k (S
2(add, k=>, 1)) ) ◦ AS
i(S
2 (add, i=>, 1)) )
4) Довести коректність програми
а) Часткова коректність
Нехай st = [am, bn], m>0, n≥0. Тоді необхідно показати, що якщо
Sem_P(a^b)(st)↓ = str, то r(str) = m^n.
Для зручності позначимо: p1 = S2(neq, a, k) ,
f1 = ASr(S
2(add, r, p)) ◦ AS
k(S
2(add, k, 1)) , mul(a, r) = WH(p1, f1) ,
p2 = S2(leq, i, b) , f2 = AS
k(1) ◦ AS
p(r) ◦ mul(a, r) ◦ AS
i(S
2 (add, i, 1))
Тоді програму можна записати у вигляді:
Sem_P(a^b) = ASi(1) ◦ AS
r(1) ◦ WH(p2, f2)
Спочатку покажемо, що mul(a, r) ([aa0, kk0, rr0, pp0]) = [aa0,
ka0, rr0+(a0–k0)*p0, pp0] , для a0–k0 ≥ 0. Доведемо це індукцією за
величиною t = (a0–k0) = (a – k). Позначимо st0 = [aa0, kk0, rr0, pp0].
База індукції: t = 0. У цьому випадку st0 = [aa0, ka0, rr0, pp0].
Перевіримо: mul(a, r) (st0) = WH(p1, f1) (st0) = WH(S2(neq, a, k), f1) (st0) = |
neq(a, k) (st0) = neq(a0, a0) = false | = st0 = [aa0, ka0, rr0+(a0–k0)*p0,
pp0]. Отже, базу індукції доведено.
Крок індукції: припустимо, що для всіх t H (H ≥ 0) виконується припущення
індукції mul(a, r) ([aa0, kk0, rr0, pp0]) = [aa0, ka0, rr0+(a0–k0)*p0,
pp0] , для a0–k0≥0 і покажемо, що при t = H+1 припущення також вірне.
Обчислимо mul(a, r) (st0), де a0–k0 = H+1 (H ≥ 0): mul(a, r) (st0) = WH(S2(neq,
a, k), f1) (st0) = | neq(a, k) (st0) = neq(a0, a0 – (H+1)) = false | = ( f1 ◦ mul(a,
r) ) (st0) = mul(a, r) (f1(st0)) = mul(a, r) ( (ASr(S
2(add, r, p)) ◦ AS
k(S
2(add, k,
1)) ) (st0) ) = mul(a, r) ([aa0, k k0+1 , rr0+p0, pp0]) = | тепер (a – k) = a0
– (k0+1) = a0 – (a0– H) = H, отже можна застосувати припущення індукції для
42
t = H | = [aa0, ka0, r(r0+p0)+(a0–(k0+1))*p0, pp0] = [aa0, ka0,
rr0+(a0–k0)*p0, pp0]. Отже, для t = H+1 припущення також вірне.
Тепер покажемо, що WH(p2, f2) ([aa0, bb0, rr0, ii0]) = [aa0,
bb0, rr0*a0^(b0–i0+1), ib0+1 , kkx, ppx] для b0–i0+1≥0. Доведемо це
твердження індукцією за величиною t = (b0–i0+1) = (b – i + 1). Позначимо
st0 = [aa0, bb0, rr0, ii0].
База індукції: t = 0. У цьому випадку st0 = [aa0, bb0, rr0, ib0+1].
Перевіримо: WH(p2, f2) (st0) = WH(S2(leq, i, b), f2) (st0) = | leq(i, b) (st0)
= leq(b0+1 , b0) = false | = st0 = [aa0, bb0, rr0*a0^(0), ib0+1] = [aa0, bb0,
rr0*a0^(b0–i0+1), ib0+1] . Отже, базу індукції доведено.
Крок індукції: припустимо, що для всіх t H (H ≥ 0) виконується припущення
індукції WH(p2, f2) ([aa0, bb0, rr0, ii0]) = [aa0, bb0, rr0*a0^(b0–
i0+1), ib0+1 , kkx, ppx] , для 0 b0–i0+1 H (тобто 0 b – i + 1 H) і
покажемо, що при t = H+1 припущення також вірне. Обчислимо WH(p2,
f2) (st0), де b0–i0+1 = H+1 (H ≥ 0) у стані st0: WH(p2, f2) (st0) = WH(S2(leq, i,
b), f2) (st0) = | leq(i, b) (st0) = leq(i0, b0) = true, оскільки i0 = b0 – H у st0 | = (
f2 ◦ WH(p2, f2) ) (st0) = WH(p2, f2) (f2(st0)) = WH(p2, f2) (ASk(1) ◦ AS
p(r) ◦
mul(a, r) ◦ ASi(S
2(add, i, 1)) (st0)) = WH(p2, f2) (AS
i(S
2(add, i, 1)) ( mul(a, r) (
[aa0, bb0, rr0, ii0, k1 , pr0] ) ) ) = WH(p2, f2) ( ASi(S
2(add, i, 1))
([aa0, bb0, rr0+(a0–1)*r0, ii0, ka0, pr0]) ) = WH(p2, f2) ([aa0, bb0,
ra0*r0, ii0+1 , ka0, pr0]) = | тепер t = b – i + 1 = b0–( i0+1)+1 = H, отже
можна скористатися припущенням індукції та отримати наступне | = [aa0,
bb0, ra0*r0*a0^(b0–(i0+1)+1), ib0+1 , kkx, ppx] = [aa0, bb0,
rr0*a0^(b0–i0+1), ib0+1 , kkx, ppx], що і потрібно було довести. Отже, для
t = H+1 припущення також вірне.
Тепер, Sem_P(a^b) (st) = ASi(1) ◦ AS
r(1) ◦ WH(p2, f2) (st) = WH(p2, f2) (
ASr(1) ( AS
i(1) ( [am, bn] ) ) ) = WH(p2, f2) ( [am, bn, i1 , r1] ) =
[am, bn, r1*m^(n–1+1), in+1 , kkx, ppx] = [am, bn, rm^n,
in+1 , kkx, ppx] = str, тобто r(str) = m^n. Ми отримали очікуваний
результат обчислень за програмою.
Отже, твердження доведене і програма є частково коректною.
б) Тотальна коректність
Покажемо, що обчислення за програмою завжди зупиняються.
43
Для початку доведемо завершуваність циклу WH(p1, f1). Позначимо
st0=st (початковий стан для циклу WH(p1, f1)), st1=f1(st0), st2=f1(st1) і т.д. Для
того, щоб цикл завершувався, потрібно показати, що існує такий стан, на якому
виконується умова a = k або a – k = 0. Припустимо, що такого стану не
існує. Тоді розглянемо послідовність різниць цих змінних, яка має бути
нескінченою:
t0 = a(st0) – k(st0), t1 = a(st1) – k(st1), t2 = a(st2) – k(st2), …
Очевидно, що t0 ≥ 0 в початковому стані st0, і те, що t0 > t1 > t2 … , тобто
послідовність є строго спадною. Тоді ця послідовність не є нескінченою,
оскільки вона обмежена знизу числом 0 (різницею однакових чисел), що і треба
було довести.
Аналогічно покажемо завершуваність циклу WH(p2, f2). Позначимо st0=st
(початковий стан для циклу WH(p2, f2)), st1=f2(st0), st2=f2(st1) і т.д. Для того,
щоб цикл завершувався, потрібно показати, що існує такий стан, на якому
виконується умова i > b або b – i < 0. Припустимо, що такого стану не
існує. Тоді розглянемо послідовність різниць таких змінних, яка має бути
нескінченою:
t0 = b(st0) – i(st0), t1 = b(st1) – i(st1), t2 = b(st2) – i(st2), …
Відомо, що b не змінює своє значення протягом виконання циклу і є
фіксованим невід’ємним значенням. Оскільки змінна i на кожній ітерації
збільшує своє значення на 1, то можна стверджувати, що t0 > t1 > t2 … , тобто
послідовність є строго спадною. Тоді ця послідовність не є нескінченою,
оскільки вона обмежена знизу числом 0, отже, змінна i набуде значення змінної
b – що і потрібно було показати.
Таким чином, з часткової коректності та факту зупинки циклів
(внутрішнього та зовнішнього) випливає тотальна коректність програми, що
розглядається.
1.6. Розширення мови SIPL: Введення булевих змінних
1.6.1.
Синтаксис розширеної мови SIPL можна задати за допомогою наступної
БНФ (див. Табл.1.4), доповнивши базову (див. п.1.1) новими правилами та
розширивши існуючі з урахуванням нових конструкцій:
Таблиця 1.4.
44
Ліва частина
правила –
метазмінна
(дефінієндум)
Права частина правила
(дефінієнс)
Ім’я
правила
<програма> ::= begin <оператор> end NP1
<оператор> ::=
<змінна_чис>:=<вираз_чис> |
<змінна_бул>:=<вираз_бул> |
<оператор> ; <оператор> |
if <вираз_бул> then <оператор> else <оператор> |
while <вираз_бул> do <оператор> |
begin <оператор> end |
skip
NS1
NS2
NS3
NS4
NS5
NS6
NS7
<вираз_чис> := <число> | <змінна_чис> |
<вираз_чис > + <вираз_чис > | <вираз_чис > –
<вираз_чис > | <вираз_чис > * <вираз_чис > |
<вираз_чис > ÷ <вираз_чис > | (<вираз_чис>)
NA1_1–
NA1_7
<вираз_бул> ::= <бул> |<змінна_бул>|
<вираз_чис> < <вираз_чис>| <вираз_чис>
<вираз_чис> | <вираз_чис> = <вираз_чис> |
<вираз_чис> <вираз_чис> | <вираз_чис> >
<вираз_чис> | <вираз_чис> <вираз_чис> |
<вираз_бул> < <вираз_бул> |<вираз_бул>
<вираз_бул> | <вираз_бул> = <вираз_бул> |
<вираз_бул> <вираз_бул> | <вираз_бул> >
<вираз_бул> | <вираз_бул> <вираз_бул> |
<вираз_бул> <вираз_бул> | <вираз_бул>
<вираз_бул> | <вираз_бул>| (<вираз_бул>)
NA2_1–
NA2_17
<змінна_чис> ::= M | N | . . . NV1–…
<змінна_бул> ::= M_B | N_B | . . . NV1–…
<число> ::= . . . –1 | 0 | 1 | 2 | 3 | . . . NN1–…
<бул> ::= true |false NB1-NB2
В даному випадку множину всіх виразів розділено на булеві та
цілочисельні. Введемо позначення для всіх синтаксичних категорій та нові
метазмінні, що вказують на представників цих категорій (див. Табл.1.5).
Таблиця 1.5.
45
Метазмінна Синтаксична категорія Нова метазмінна
<програма> Prog P
<оператор> Stm S
<вираз_чис> Aexp a
<вираз_бул> Bexp c
<змінна_чис> Var x
<змінна_бул> Var_B y
<число> Num n
<бул> Bool b
В наведених позначеннях БНФ мова SIPL набуває наступного вигляду
(Табл.1.6):
Таблиця 1.6.
Ліва частина
правила –
метазмінна
(дефінієндум)
Права частина правила
(дефінієнс)
Ім’я
правила
P ::= begin S end NP1
S ::=
x:=a|
y:=c|
S 1; S2|
if c then S1 else S2 |
while c do S |
begin S end |
skip
NS1
NS2
NS3
NS4
NS5
NS6
NS7
a := n | x |
a 1+ a2 | a1 – a2 | a1 * a2 | a1 ÷ a2 | (a)
NA1_1–
NA1_7
c ::= b |y|
a 1< a2| a1 a2 | a1 = a2 | a1 a2 | a1 > a2 | a1
a2 |
c1 < c2 |c1 c2 | c1 = c2 | c1 c2 | c1 > c2 | c1 c2 |
c1 c2 | c1 c2 | c| (c)
NA2_1–
NA2_17
46
x ::= M | N | . . . NV1–…
y ::= M_b | N_b | . . . NV1–…
n ::= . . . –1 | 0 | 1 | 2 | 3 | . . . NN1–…
b ::= true |false NB1-NB2
Надалі будемо користуватися введеними позначеннями для запису
структури програм та їх складових.
Базові типи даних – множини цілих чисел, булевих значень та змінних
(імен):
Int={ . . . , -2, -1, 0, 1, 2, . . . }
Bool={true, false}
Var={X,Y, … }
Var_B={X_B,Y_B, … }
Похідні типи – множина станів змінних (наборів іменованих значень,
наборів змінних з їх значеннями):
State={Var Int,Var_B Bool}
Приклади станів змінних: [M8, N16], [M3, X 4, Ytrue, Nfalse].
Операції на множині Int. Символам +, – , , відповідають операції add,
sub, mult, div (додавання, віднімання, множення, ділення). Це бінарні операції
типу Int2 Int.
Операції на множині Bool. Символам , , відповідають операції or,
and, neg. Це бінарні операції типу Bool2 Bool (диз’юнкція та кон’юнкція) та
унарна операція типу Bool Bool (заперечення).
В мові також є операції змішаного типу. Символам операцій порівняння
<, , =, , , > відповідають операції less, leq, eq, neq, geq, gr. Це бінарні
операції типу Int2 Bool.
Крім того, введемо
функцію-константу арифметичного типу n : StateInt, таку що
n (st)=n,
функції-константи бульового типу true , false : StateBool такі, що
true (st)=true, false (st)=false,
тотожну функцію id: StateState, таку, що id(st)=st.
Отримали багатоосновну алгебру даних мови SIPL
A_Int_Bool_State =<Int, Bool, State; add, sub, mult, div, or, and, neg,
less, leq, eq, neq, geq, gr, x, x, y, y, n , b , id, , true , false >
Формули обчислення задаються аналогічно алгебрі A_Prog.
Нові правила побудови семантичного терму подано у табл.1.7:
Таблиця 1.7.
47
Правило заміни Номер
правила
sem_P: Prog FS задається правилами:
sem_P(begin S end)= sem_S(S) NS_Prog
sem_S: Stm FS задається правилами:
sem_S(x:=a)=ASx(sem_A(a))
sem_S(y:=c)=ASy(sem_B(c))
sem_S(S1;S2)= sem_S(S1) sem_S(S2)
sem_S(if c then S1 else S2)= IF(sem_B(b),
sem_S(S1), sem_S(S2))
sem_S(while c do S)= WH(sem_B(b), sem_S(S))
sem_S(begin S end)=(sem_S(S))
sem_S(skip)=id
NS_Stm_As
NS_Stm_As_B
NS_Stm_Seq
NS_Stm_If
NS_Stm_Wh
NS_Stm_BE
NS_Stm_skip
sem_A: Aexp FA задається правилами:
sem_A(n))= n
sem_A(x))=x
sem_A(a1+a2)=S2(add, sem_A(a1), sem_A(a2))
sem_A(a1–a2)=S2(sub, sem_A(a1), sem_A(a2))
sem_A(a1a2)=S2(mult, sem_A(a1), sem_A(a2))
sem_A(a1a2)=S2(div, sem_A(a1), sem_A(a2))
sem_A((a))=sem_A(a)
NS_A_Num
NS_A_Var
NS_A_Add
NS_A_Sub
NS_A_Mult
NS_A_Div
NS_A_Par
sem_B: Bexp FB задається правилами:
48
Правило заміни Номер
правила
sem_B(b)= b
sem_B(y)=y
sem_B(a1<a2)=S2(less, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(leq, sem_A(a1), sem_A(a2))
sem_B(a1=a2)=S2(eq, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(neq, sem_A(a1), sem_A(a2))
sem_B(a1a2)=S2(geq, sem_A(a1), sem_A(a2))
sem_B(a1>a2)=S2(gr, sem_A(a1), sem_A(a2))
sem_B(c1<c2)=S2(less, sem_B(c1), sem_B(c2))
sem_B(c1c2)=S2(leq, sem_B(c1), sem_B(c2))
sem_B(c1=c2)=S2(eq, sem_B(c1), sem_B(c2))
sem_B(c1c2)=S2(neq, sem_B(c1), sem_B(c2))
sem_B(c1c2)=S2(geq, sem_B(c1), sem_B(c2))
sem_B(c1>c2)=S2(gr, sem_B(c1), sem_B(c2))
sem_B(c1c2)=S2(or, sem_B(c1), sem_B(c2))
sem_B(c1c2)=S2(and, sem_B(c1), sem_B(c2))
sem_B(c)=S1(neg, sem_B(c))
sem_B((c))= sem_B(c)
NS_B_Const
NS_B_Var
NS_B_less
NS_B_leq
NS_B_eq
NS_B_neq
NS_B_geq
NS_B_gr
NS_B_lessB
NS_B_leqB
NS_B_eqB
NS_B_neqB
NS_B_geqB
NS_B_grB
NS_B_or
NS_B_and
NS_B_neg
NS_B_Par
1.7. Приклади розв’язків задач.
Припустимо, що у SIPL введено лише базові необхідні предикати: , , .
Виразимо решту предикатів , =, >, , <, через базові: у змінну res записати
результат обчислення відповідного предикату над значеннями вхідних змінних
A та B.
Завдання 1.7. Розписати через і .
begin
if B)A( then res:=true
else res:=false
end
Побудуємо дерево виводу:
49
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if B)A( then res:=true else
res:=false)=IF(S1(neg,S
2(or,S
1(neg,A=>),S
1(neg,B=>))),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
Маємо початкове дане d=[A а ,B b ].
Sem_P(P)(d)= IF(S1(neg,S
2(or,S
1(neg,A=>),S
1(neg,B=>))),AS
res( true ),
ASres
( false ))(d)=
=
false)))(d)B(neg,S),A(neg,S(or,S(neg,S якщо)(d),false(AS
true)))(d)B(neg,S),A(neg,S(or,S(neg,S якщо)(d),true(AS
1121res
1121res
=
=
false)))b(neg,S),a(neg,S(or,S(neg,S якщо],false[resd
true)))b(neg,S),a(neg,S(or,S(neg,S якщо],true[resd
1121
1121
=
=
false)))b(neg,S),a(neg,S(or,S(neg,S якщо],falseres,bB, a[A
true)))b(neg,S),a(neg,S(or,S(neg,S якщо],trueres,bB, a[A
1121
1121
=
false)ba( якщо],falseres,bB, a[A
true)ba( якщо],trueres,bB, a[A
=
falseba якщо],falseres,bB, a[A
trueba якщо],trueres,bB, a[A
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
Завдання 1.8. Розписати = через ≤
begin
if X)(YY)(X then res:=true
else res:=false
<програма>
begin <оператор> end
if <умова
> then <оператор> else <оператор>
┐ <умова>
( <умова
> )
<умова
>
<умова
>
<умова
>
<вираз
> <змінна
> А
┐ <умова
>
<вираз
> <змінна
> В
┐
<змінна
>
:= <вираз
>
<константа
>
res
<true>
<змінна
>
:= <вираз
>
<константа
>
res
<false
>
50
end
Побудуємо дерево виводу:
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if X)(YY)(X then res:=true else res:=false)=
=IF(S2(and,S
2(leq,X=>, Y=>),S
2(leq, Y=>, X=>)),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
d=[X x ,Y y ]
Sem_P(P)(d)=
=IF(S2(and,S
2(leq,X=>, Y=>),S
2(leq, Y=>, X=>)),AS
res( true ), AS
res( false ))(d)=
=
false=>))(d)X =>,Y (leq,S=>),Y =>,X(leq,S(and,S якщо)(d),false(AS
true=>))(d)X =>,Y (leq,S=>),Y =>,X(leq,S(and,S якщо)(d),true(AS
222res
222res
=
=
false))x ,y (leq,S),y ,x(leq,S(and,S якщо],false[resd
true))x ,y (leq,S),y ,x(leq,S(and,S якщо],true[resd
222
222
=
=
false))x y( )y x(( якщо],falseres,yY, x[X
true))x y( )y x(( якщо],trueres,yY, x[X
=
false)y x( якщо],falseres,yY, x[X
true)yx( якщо],trueres,yY, x[X
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
Завдання 1.9. Розписати >
begin
if Y)(X then res:=true
else res:=false
end
<програма
>
begin <оператор> end
if <умова
> then <оператор> else <оператор>
<змінна
>
:= <вираз
>
<константа
>
res
<true>
<змінна
>
:= <вираз
>
<константа
>
res
<false
>
<вираз
> <змінна
> Y
<умова
> ) (
<вираз
> <змінна
> X
<умова
>
<вираз
> <змінна
> X
<умова
> ) (
<вираз
> <змінна
> Y
<умова
>
51
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if Y)(X then res:=true else res:=false)=
=IF(S1(neg,S
2(leq,X=>, Y=>)),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
d=[X x ,Y y ]
Sem_P(P)(d)=IF(S1(neg,S
2(leq,X=>, Y=>)),AS
res( true ), AS
res( false ))(d)=
=
false=>))(d)Y =>,X(leq,S(neg,S якщо)(d),false(AS
true=>))(d)Y =>,X(leq,S(neg,S якщо)(d),true(AS
21res
21res
=
=
false))y ,x(leq,S(neg,S якщо],false[resd
true))y ,x(leq,S(neg,S якщо],true[resd
21
21
=
=
false)y x( якщо],falseres,yY, x[X
true)y x( якщо],trueres,yY, x[X=
=
falsey x якщо],falseres,yY, x[X
truey x якщо],trueres,yY, x[X
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
Завдання 1.10. Розписати ≠
begin
if Y)(X then res:=true
else res:=false
end
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if Y)(X then res:=true else res:=false)=
=IF(S1(neg,S
2(eq,X=>, Y=>)),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
d=[X x ,Y y ]
Sem_P(P)(d)=IF(S1(neg,S
2(eq,X=>, Y=>)),AS
res( true ), AS
res( false ))(d)=
=
false=>))(d)Y =>,X(eq,S(neg,S якщо)(d),false(AS
true=>))(d)Y =>,X(eq,S(neg,S якщо)(d),true(AS
21res
21res
=
=
false))y ,x(eq,S(neg,S якщо],false[resd
true))y ,x(eq,S(neg,S якщо],true[resd
21
21
=
=
false)yx( якщо],falseres,yY, x[X
true)yx( якщо],trueres,yY, x[X=
52
=
falseyx якщо],falseres,yY, x[X
trueyx якщо],trueres,yY, x[X
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
Завдання 1.11. Розписати <
begin
if Y)(XY)(X then res:=true
else res:=false
end
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if Y)(XY)(X then res:=true else res:=false)=
=IF(S2(and,S
2(leq,X=>, Y=>),S
2(neq, X=>, Y=>)),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
d=[X x ,Y y ]
Sem_P(P)(d)=
=IF(S2(and,S
2(leq,X=>, Y=>),S
2(neq, X=>, Y=>)),AS
res( true ), AS
res( false ))(d)=
=
false=>))(d)Y =>,X (neq,S=>),Y =>,X(leq,S(and,S якщо)(d),false(AS
true=>))(d)Y =>,X (neq,S=>),Y =>,X(leq,S(and,S якщо)(d),true(AS
222res
222res
=
=
false))y,x (neq,S),y ,x(leq,S(and,S якщо],false[resd
true))y,x (neq,S),y ,x(leq,S(and,S якщо],true[resd
222
222
=
=
false)yx()yx( якщо],falseres,yY, x[X
true)yx()yx( якщо],trueres,yY, x[X=
=
false)yx( якщо],falseres,yY, x[X
true)yx( якщо],trueres,yY, x[X
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
Завдання 1.12. Розписати ≥
begin
if Y)(XY)(X then res:=true
else res:=false
end
Побудуємо семантичний терм:
Sem_P(P)=Sem_S(if Y)(XY)(X then res:=true else res:=false)=
53
=IF(S2(or,S
2(gr,X=>, Y=>),S
2(eq, X=>, Y=>)),AS
res( true ), AS
res( false ))
Доведемо часткову коректність:
d=[X x ,Y y ]
Sem_P(P)(d)=IF(S2(or,S
2(gr,X=>, Y=>),S
2(eq, X=>, Y=>)),AS
res( true ),
ASres
( false ))(d)=
=
false=>))(d)Y =>,X (eq,S=>),Y =>,X(gr,S(or,S якщо)(d),false(AS
true=>))(d)Y =>,X (eq,S=>),Y =>,X(gr,S(or,S якщо)(d),true(AS
222res
222res
=
=
false))y ,x (eq,S),y ,x(gr,S(or,S якщо],false[resd
true))y ,x (eq,S),y ,x(gr,S(or,S якщо],true[resd
222
222
=
=
false)yx()yx( якщо],falseres,yY, x[X
true)yx()yx( якщо],trueres,yY, x[X=
=
false)yx( якщо],falseres,yY, x[X
true)yx( якщо],trueres,yY, x[X
Часткову коректність доведено.
Програма не має циклів, тому вона завершується. А оскільки часткова
коректність доведена, то маємо тотальну коректність.
1.8. Розширення мови SIPL: Введення викликів функцій
Подамо у наступній таблиці (Табл.1.8) cинтаксис розширення БНФ:
Таблиця 1.8.
Ліва частина
правила –
метазмінна
Права частина правила
Ім’я
правила
<програма> ::= …|
program <список об’явлень функцій>
begin <оператор> end
NP1
NP2
<вираз> ::= …|
if <умова> then <вираз> else <вираз> | <виклик
функції>
NA1–NA7
NA8
NA9
<список
об’явлень
функцій>::=
|
<об’явлення функції> | <об’явлення функції> ;
<список об’явлень функцій>
LDF1
LDF2
LDF3
<об’явлення
функції>::=
func <ім’я функції>=<вираз> |
func <ім’я функції>(<список формальних
параметрів>) = <вираз>
DF1
DF2
<ім’я функції> ::= <змінна> NF
54
<список
формальних
параметрів>::=
<змінна> |
<змінна> , <список формальних параметрів>
LFP1
LFP2
<виклик
функції> ::=
<ім’я функції> |
<ім’я функції> (<список фактичних
параметрів>)
CF1
CF2
<список
фактичних
параметрів>::=
<вираз> | <вираз> , <список фактичних
параметрів>
LAP1
LAP2
Тобто ми змінили синтаксис виразу: додавши варіант завдання виразу за
допомогою умов
<вираз> ::= if <умова> then <вираз1> else <вираз2>
якщо умова виконується, то <вираз> := <вираз1> інакше <вираз> :=
<вираз2>, або ж
<вираз> ::= <виклик функції>
значення виразу буде дорівнювати результату виклику функції.
Зміниться і синтаксис програми: додамо варіант
<програма> ::= program <список об’явлень функцій> begin <оператор> end
де <список об’явлень функцій> може бути порожнім, або складатися з
об’явлень функцій перерахованих через кому. Так ми додамо можливість
використовувати функції із списку об’явлень функцій
Синтаксис об’явлення функції митаме такий вигляд:
<об’явлення функції>::= func <ім’я функції>=<вираз> |
func <ім’я функції>(<список формальних параметрів>) = <вираз>
де список формальних параметрів це список змінних перерахованих через
кому.
Синтаксис виклику функції має вигляд:
<виклик функції> ::= <ім’я функції> , якщо функція не має параметрів, та
<виклик функції> ::= <ім’я функції> (<список фактичних параметрів>) де
список фактичних параметрів – список виразів перерахованих через кому.
Семантика суперпозиції при цьому може бути 2-х видів:
Глобальною суперпозицією функцій g1,g2,…,gn в номінативну функцію f
називається функція, яка задається формулою:
S(v1,v2,..vn)(f,g1,g2,…,gn)(st) = f([v1g1(st),v2g2(st),…,vngn(st)])
Локальною суперпозицією функцій g1,g2,…,gn в номінативну функцію f
називається функція, яка задається формулою
S[v1,v2,..vn](f,g1,g2,…,gn)(st) = f(st[v1g1(st),v2g2(st),…,vngn(st)])
+ семантика – формально + повно
…
55
1.5. пункти і т.д. = оформлення: шрифт (14) + абзаци (відступи) + підписи
таблиць (стандарт) + відступи у тексті + міжстрокові…
1.9. Приклади задач
…
1.10. Завдання для самостійної роботи
…
1.11. Приклади завдань для контрольної роботи.
…
56
Тема 2. Формальні мови та граматики
2.1. Побудова мови за допомогою системи рівнянь з регулярними
коефіцієнтами
Завдання 2.1. Нехай задано мову L, яку описує регулярний вираз r = a*ba
*.
Побудувати за регулярним виразом граматику і систему лінійних рівнянь,
розв’язати систему.
Розв’язок.
1) Побудуємо праволінійну граматику G для мови L. G = <VT, VN, Pr,
S>.
VT = {a, b}; VN = {S, S1, S2}; Pr = {S->aS1; S->bS2; S1->aS1; S1->bS2; S2->aS2; S2-
>ε}, S ={S}
Отже G = < {a, b}; {S, S1, S2}; {S->aS1; S->bS2; S1->aS1; S1->bS2; S2->aS2; S2->ε},
{S}>
2) Тепер побудуємо систему лінійних рівнянь для цієї граматики:
Кількість рівнянь дорівнює кількості нетерміналів. В нашому випадку 3.
Кожному нетерміналу ставимо у відповідність змінну : S-> X1; S1->X2; S2->X3.
Система має такий вигляд:
X1 = p1+ q11X1 + q12X2 + q13X3
X2 = p2+ q21X1 + q22X2 + q23X3
X3 = p3+ q31X1 + q32X2 + q33X3
pi визначається так : для всіх продукцій вигляду Xi ->γj, pi = k
k . Всі qij
визначаються так: для всіх продукцій вигляду Xi ->γjXj, qij = k
k . Маємо:
X1 = X2a + X3b
X2 = X2a + X3b
X3 = ε + X3a
Зауважимо, що розв’язком рівняння виду X = Xα + β буде регулярний
вираз βα*, що доводиться підстановкою. Систему рівнянь розв’язуємо методом
Гауса. Розв’язком системи ми вважаємо Xk вираз при змінній, яка відповідає
57
нетерміналу аксіоми. В нашому випадку буде регулярній вираз при змінній X1.
Робимо послідовні підстановки:
X1 = X2a + X3b підставляємо у всі рівняння. Рівняння X2 = X2a + X3b має
розв’язок X2=(X3b)a*. Підставляємо X2 у останнє рівняння. Маємо X3 = ε + X3a.
Тепер робимо зворотню підстановку: розв’язком останнього рівняння буде X3 =
εa* = a
*. Підставляємо X3 в перше і друге рівняння : X1 = X2a + a
*b; X2 = X2a +
a*b. Розв’язуємо друге рівняння : X2 = a
*ba
* і підставляємо в перше рівняння X1
= a*ba
*a + a
*b = a
*b(a
*a + ε) = a
*ba
*. Отже отримали регулярний вираз a
*ba
*,
який описує ту саму мову, що і граматика, побудована за початковим
регулярним виразом.
2.2. Побудова мови методом наближень
Завдання 2.2. Задано мову L, яку описує регулярний вираз r = anba
n. Побудувати
граматику і методом послідовних наближень побудувати мову.
Розв’язок.
Побудуємо породжуючу граматику G для мови L: G = <{a, b}, {S}, {S-
>aSa, S->ε}, {S}>. Покажемо, що мова, яку описує побудована граматика
складається зі слів вигляду anba
n і тільки з них.
Запишемо для цієї граматики рівняння : S = {b}{a}S{a}. Тоді функція
φ(Li) = {b}{a}Li{a}.
Розглянемо L0, як перше наближення мови L : L0 = {b}. Застосовуючи
метод послідовних наближень маємо L1 = φ(L0) = {b}{a}{b}{a} = {b}{aba}.
Нехай Lk=k
i
iibaa0
}{
, тоді Lk+1 = φ(Lk) = φ(k
i
iibaa0
}{
) = {b}{a}k
i
iibaa0
}{
{a} =
{b}1
0
}{
k
i
iibaa = 1
0
}{
k
i
iibaa .
Очевидно, що послідовність (Lk) – зростаюча, отже існує границя –
L′=
0
}{i
ii
k
k baaL . Отже L містить у собі L′ (це очевидно, бо для кожного слова
w мови L′ існує таке невід’ємне n, що слово w = anba
n ).
Покажемо зворотне включення. Візьмемо довільне слово w з мови L.
Зрозуміло, що це слово має вигляд : w = anba
n. Покажемо, що це слово
належить мові L′. Це слідує з існування виводу цього слова у граматиці. Для
виводу слова необхідно застосувати до аксіоми S правило S->aSa n разів, а
58
потім правило S->b один раз. Отже мови L і L′ однакові і граматика G задає
мову L і тільки її.
2.3. Побудова граматики за мовою
Завдання 2.3. Нехай задано мову L = {anb
n|n>=0}. Побудувати граматику і
показати, що вона породжує усі слова мови і тільки їх.
Розв’язок.
1) Побудуємо граматику G для мови L. G = <VT, VN, Pr, S>.
VT = {a, b}; VN = {S}; Pr = {S→aSb; S→ε}, S ={S}.
Отже G = < {a, b}; {S}; {S→aSb; S→ε}, {S}>
2) Перевіримо, що граматика породжує всі слова мови.
Поділимо все, що породжує граматика на такі типи (класи):
1) akSb
k
2) akb
k , k>=0
Можливо, доведеться їх змінити. Адже нам треба отримати такі класи, щоб
застосування правил виведення граматики не виводило нас за їх межі.
Тепер побудуємо наступну таблицю:
типи 1
2
правила
1: S→aSb 1 -
2: S→ε 2 -
Тобто, застосування 1-го правила до 1-го типу залишає слово у 1 типі.
2-го правила до 1-го типу – переводить у 2-ий тип.
Жодне правило не застосовне до слів 2-го типу(-).
1тип.1пр) akSb
k →
1 a
kaSbb
k = a
(k+1)Sb
(k+1)
1.2) akSb
k →
2 a
kb
k
Таким чином, ми поділили все, що породжує граматика на «замкнені у
своїх межах» класи. Тобто все те, що вона породжує, не виходить за їх межі.
3) Порівняємо тепер вихідну мову, і мову, породжену побудованою
граматикою.
(akSb
k)∩VT*=Ø
(akb
k , k>=0)∩VT*= a
kb
k , k>=0
Тобто ця граматика породжує усі слова мови і тільки їх.
59
Завдання 2.4. Нехай задано мову L = {anb
nc
n|n>=0}. Побудувати граматику і
показати, що вона породжує усі слова мови і тільки їх.
Розв’язок.
1) Побудуємо граматику G для мови L. G = <VT, VN, Pr, S>.
VT = {a, b, c}; VN = {S; B}; Pr = {S→aBSc; S→ε; Ba→aB; Bc→bc; Bb→bb}, S
={S}.
Отже G = < {a, b, c}; {S; B}; {S→aBSc; S→ε; Ba→aB; Bc→bc; Bb→bb}, {S}>
2) Перевіримо, що граматика породжує всі слова мови.
Поділимо все, що породжує граматика на такі типи(класи):
1) (aB)nSc
n
2) ycn , де |y|a = |y|B = n, y = (a|B)*
3) ycn , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b
Можливо, доведеться їх змінити. Адже нам треба поділити на класи так, щоб
застосування правил виведення граматики не виводило нас за межі цих класів.
Тепер побудуємо наступну таблицю:
типи 1 2 3
правила
1: S→aBSc 1 - -
2: S→ε 2 - -
3: Ba→aB 1 2 3
4: Bc→bc - 3 -
5: Bb→bb - - 3
Тобто, застосування 1-го правила до 1-го типу залишає слово у 1 типі.
2-го правила до 1-го типу – переводить у 2-ий тип.
1тип.1пр)(aB)nSc
n →
1 (aB)
naBScc
n = (aB)
(n+1)Sc
(n+1)
1.2) (aB)nSc
n →
2 (aB)
nc
n = yc
n , де |y|a = |y|B = n, y = (a|B)*
1.3) (aB)nSc
n →
3 ySc
n , де |y|a = |y|B = n, y = (a|B)*
Так як клас yScn , де |y|a = |y|B = n, y = (a|B)*, який ми отримали внаслідок
застосування правила 3 до 1-го класу включає у себе клас 1 ((aB)nSc
n), тому
змінимо клас 1 на цей отриманий клас. Попередні правила та виводи не
зміняться.
Правила 4 та 5 незастосовні до класу 1.
Правила 1, 2, 5 незастосовні до класу 2.
2.3) ycn →
3 yc
n , де |y|a = |y|B = n, y = (a|B)*
60
2.4) ycn →
3 qc
n , де |y|a = |y|B = n, y = (a|B)*, |q|a = |q|b + |q|B = n, q = (a|B)*b*b
Правила 1, 2, 4 незастосовні до класу 3.
3.3) ycn →
3 yc
n , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b
3.5) ycn →
3 yc
n , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b
Таким чином, ми поділили все, що породжує граматика на «замкнені у своїх
межах» класи.
1) yScn , де |y|a = |y|B = n, y = (a|B)*
2) ycn , де |y|a = |y|B = n, y = (a|B)*
3) ycn , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b
Тобто все те, що вона породжує, не виходить за їх межі.
3) Порівняємо тепер вихідну мову, і мову, породжену побудованою
граматикою.
(yScn , де |y|a = |y|B = n, y = (a|B)*)∩T*=Ø
(ycn , де |y|a = |y|B = n, y = (a|B)*)∩T*= {ε}
(ycn , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b)∩T* = yc
n , де |y|a = |y|b + |y|B = n, y =
(a|B)*b*, причому |y|B = 0, тому y = a*b*b= anb
n.
Тобто:
(ycn , де |y|a = |y|b + |y|B = n, y = (a|B)*b*b)∩T* = a
nb
nc
n .
Отже, ця граматика породжує усі слова мови і тільки їх.
Завдання 2.5.
1) Побудувати породжувальну граматику G для мови ;| nmcbaL nmn
2) довести, що ;)( LGL
3) довести, що мова L не є КВ мовою.
Розв’язок.
1) Граматика G для мови ;| nmcbaL nmn має наступні правила виводу:
bbBb
aBBa
bBS
BSS
aBScS
61
2) Доведемо, що LGL )(
В граматиці G можна побудувати наступні види слів:
nmcba
nmmnbBac
nmmnBabc
nmmnBaSc
nmn
bBa
n
Ba
n
Ba
n
,)4
,,*,*)|(,)3
,1,)*,|(,)2
,,)*,|(,)1
Покажемо, що це всі можливі види слів, що породжуються в граматиці – для
цього відобразимо, як правила виводу перетворюють один вигляд в інший,
таким чином показавши замкненість множини слів (точніше, їх видів) відносно
виведення:
типи слів
правила
виводу
1 2 3 4
1 1 - - -
2 1 - - -
3 2 - - -
4 1 2 3 -
5 - 3 3,4 -
Отже, очевидно, що в граматиці виводяться лише слова 4 типу.
Таким чином доведено LGL )( . Доведемо тепер LGL )( .
Покажемо можливість виводу будь-якого слова з L.
Вивід слова nmn cba з аксіоми S повинен бути наступним:
1) Правило 1) застосовується n разів.
2) Правило 2) застосовується m-n разів.
3) Правило 3) застосовується 1 раз.
4) Правило 4) застосовується, поки застосовне ((n-1)*(n-2)/2 разів).
5) Правило 5) застосовується m-1 раз.
3) Довести, що мова L не є КВ мовою.
Якби мова L була КВ-мовою, то існували б ланцюжки u, v, t1, t2, x {a*,b**,c*}
такі, що ut1ix t2
ivL для всіх i=0,1,…. Зрозуміло, що t1 (так само як і t2) не може
складатися з різних символів (інакше для деякого i ланцюжок ut1ix t2
iv не буде
належати L. Але якщо t1 складається лише з одного символу, то збільшуючи i
можна порушити баланс символів a,b,c. Тому мова L не може бути КВ-мовою.
62
Завдання 2.5.
Побудувати граматику у нормальній формі Хомського та Гребах для наступної
КВ-граматики:
…
Розв’язок.
63
Тема 3. Рекурсія та найменша нерухома точка. Неперервність операторів
Озн. ω-областю називаємо повну, частково впорядковану множину з
мінімальним елементом.
Озн. Ланцюгом будемо називати довільну послідовність елементів {d0, d1, …}
ω-області, що задовольняють умові : d0 ≤ d1 ≤ … , де ≤ - відношення часткового
порядку на ω-області.
Озн. Відображення φ : DD неперервне, якщо для ланцюга d0 ≤ d1 ≤ … з ω-
області D виконується wi
i
wi
i dd
)()(
Лема : Нехай φ – n - вимірне відображення : φ : DD , де D - ω-область. φ –
неперервне відображення, тоді і тільки тоді, коли φ – неперервне відносно
кожного зі своїх аргументів.
Завдання 3.1. Довести неперервність оператора IF(p, f, g), де f і g неперервні.
Розв’язок.
Доведемо неперервність IF(p, f, g) за аргументом f. Візьмемо довільний
ланцюг f0 ≤ f1 ≤ … . Доведемо рівність : )(),,())(,,( stgfpIFstgfpIFi
i
i
i
. Маємо
else
falsestpякщоdg
truestpякщоrstfk
else
falsestpякщоstg
truestpякщоstf
stgfpIF
ki
i
i
i
,
)(),(
)(,))((
,
)(),(
)(,)(
))(,,(
Враховуючи неперервність і те, що fi утворює ланцюг маємо :
else
falsestpякщоdg
truestpякщоrstf
k
else
falsestpякщоdg
truestpякщоrstfk kk
,
)(),(
)(,)(
,
)(),(
)(,))((
k
k
k
k
gfpIF
else
falsestpякщоdg
truestpякщоrstf
),,(
,
)(),(
)(,)(
Аналогічно доводиться неперервність IF(p, f, g) відносно аргументу g.
Доведемо тепер неперервність IF(p, f, g) відносно аргументу p. Візьмемо
довільний ланцюг p0 ≤ p1 ≤ … . Доведемо рівність :
)(),,())(,,( stgfpIFstgfpIFi
i
i
i
.
64
else
falsestpkякщоstg
truestpkякщоstf
else
falsestpякщоstg
truestpякщоstf
stgfpIF k
k
i
i
i
i
,
))((),(
))((),(
,
)(),(
)(),(
))(,,(2
1
2
1
Використовуємо неперервність pі і те, що pі утворюють ланцюг, покладемо
k=max(k1,k2). Маємо:
k
kk
k
k
k
gfpIF
else
falsestpkякщоstg
truestpkякщоstf
else
falsestpkякщоstg
truestpkякщоstf
),,(
,
))((),(
))((),(
,
))((),(
))((),(
2
1
2
1
Отже неперервність відносно аргументів доведеною Отже IF(p, f, g) –неперервна
функція своїх аргументів.
Завдання 3.2. Довести неперервність оператора ),,(),( fggfpIFgfF ,
де f і g неперервні.
Розв’язок.
Для того, щоб показати неперервність оператора F, нам потрібно довести його
неперервність по f і g:
1)
i
ii
i
i
i
i fggfpIFfggfpIF ),,(),,(
2)
i
ii
i
i
i
i fggfpIFfggfpIF ),,(),,(
Доведемо 1) – неперервність по f:
1)
i
ii
i
i
i
i fggfpIFfggfpIF ),,(),,(
а) truestp )(
stfggfpIF
stfggfpIFkstgfk
bgbstfkb
bgbstfb
stgfstfggfpIF
i
ii
kkk
k
i
i
i
i
i
i
i
i
),,(
,,
&)(
)(&)(
)( )(,,
65
б) falsestp )(
),,(
,,
& )(
)(& )(
)( )(,,
stfggfpIF
stfggfpIFkstfgk
bfkbstgb
bfbstgb
stfgstfggfpIF
i
ii
kkk
k
i
i
i
i
i
i
i
i
Аналогічно доводиться для 2).
66
Тема 4. Натуральна семантика
Завдання 4.1. Нехай Program(a,b) – програма знаходження результату ділення a
на b – res та залишку ділення ost (див. завдання 1.2). Обчислити за натуральною
семантикою результат застосування програми Program(a,b) до даного
]35,[ ba .
Розв’язок.
Текст програми:
Program(a,b) =
Begin
rez:= 0;
k:= 0;
if (a = 0) then skip else
if (a < b) then skip else
while k <= a – b do
begin
res:= res+1;
k:= k+b
end;
ost:= a – k
End
Спочатку введемо скорочення для фрагментів програми:
Program(a,b) =
Begin
res:=0;
k:=0;
if (a=0) then skip else
if (a<b) then skip else
while k<=a-b do
begin
res:=res+1;
k:=k+b
end;
ost:=a-k
End
P1 =
res:=0;
k:=0;
if (a=0) then skip else
if (a<b) then skip else
while k<=a-b do
begin
res:=res+1;
k:=k+b
end;
ost:=a-k
P2 =
k:=0;
if (a=0) then skip else
if (a<b) then skip else
while k<=a-b do
begin
res:=res+1;
k:=k+b
end;
ost:=a-k
67
P3 =
if (a=0) then skip else
if (a<b) then skip else
while k<=a-b do
begin
res:=res+1;
k:=k+b
end;
ost:=a-k
P4 =
if (a<b) then skip else
while k<=a-b do
begin
res:=res+1;
k:=k+b
end;
ost:=a-k
Нехай 0st = ]35,[ ba , тоді:
SEQ (0) AS
SEQ
PR
0]res3,b5,[ast1
0,0
]0[,0
,
0
0110
01
0
stNum
resststst,stres:
ststp
stb),stРrogram(a,
AS (8) IF (1)SEQ AS
SEQ (0)
false
ststp
stNum
kst,stst,stk:
ststp
23
1
1221
12
,
0,0
]0[0
,
0]k0,res3,b5,[ast2
false
true
true
false
false
false
(7)WH S()(4)
A
B
WH (3)
WH (3)
B
IF (2)
IF (2)
B
IF (1)
b,st VARa,stVAR
),sub(b,sta k,stVAR
true),leq(b,stak
stb end ,stk; k:resin res:a-b do begwhile k
stbVARstaVAR
falselessstba
ststp
stNumstaVar
falseeqsta
ststp
35
2350
20
1
3,5,
)3,5(,
,
0,05,
)0,5(,0
,
22
22
2
32
22
2
324
22
2
323
stb end,stk;k:resbegin res:
AS (6) AS (5)S() (4) 421
68
A
B
WH (7)
A
stb,k:kAS (6)
A
AS (5)
false
5
3,5
2)3,5(,3,
)2,3(,
,,1
3,0,
3)3,0(,
]3[,
1,10,
1)1,0(,1
]1[,1
44
44
4
34
55
5
544
22
2
2552
stbVAR a,stVAR
substba stkVAR
falseleqstbak
ststb end k; k:resin res:a-b do begwhile k
stbVarstkVar
addstbk
kststst
stNumstresVar
addstres
resststst,stresres:
43
4
5
stst
3]k1,res3,b5,[ast
0]k1,res3,b5,[ast
2]ost3,k1,res3,b5,[ast
3,5,
2)3,5(,
]2[,,:
33
3
3
stkVarstaVar
substka
ostststststkaost
A
AS (8) 3
Результат: 2]ost3,k1,res3,b5,[ast
Завдання 4.2. Нехай GCD(M,N) – програма обчислення найбільшого спільного
дільника за алгоритмом Евкліда (див. завдання 1.1).
Обчислити за натуральною семантикою результат застосування програми
GCD(M,N) до даного [M->19,N->8].
Розв’язок.
GCD(M,N)=
begin
while M N do
if M>N then M:=M-N else N:=N-M
end
Застосуємо програму до вхідного даного та обчислимо за натуральною
семантикою результат станом [M->19,N->8].
Початковий стан:
]8,19[0 NMst
Композиційний семантичний терм програми:
69
=>))))M=>,N (sub,(SAS
=>)),N =>,M (sub,(SAS =>),N =>,M (gr,S IF( , =>)N =>,M (neq,WH(S
2N
2M22
Отже,
)2()2.1()1.1(
),(...),...(
8,19,
)8,19(),,,(
...),),,,((
10
2
00
0
2
0
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
true
true
(1.1):
8,19,
)8,19((...),
00
0
2
stNDM
stMDM
truegrstSRS
(1.2):
8,19,
11)8,19(),,(
]11[(...),(
00
2
010
2
stNDM
stMDM
subNMsubSRS
MstststSASAS
M
]8,11[1 NMst
(2):
)3()2.2()1.2(
),(...),...(
8,11,
)8,11(),,,(
...),),,,((
21
2
11
1
2
1
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
true
true
(2.1):
8,11,
)8,11((...),
11
1
2
stNDM
stMDM
truegrstSRS
(2.2):
8,11,
3)8,11(),,(
]3[(...),(
11
2
121
2
stNDM
stMDM
subNMsubSRS
MstststSASAS
M
]8,3[2 NMst
70
(3):
)4()2.3()1.3(
),(...),...(
8,3,
)8,3(),,,(
...),),,,((
32
2
22
2
2
2
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
false
true
(3.1):
8,3,
)8,3((...),
22
2
2
stNDM
stMDM
falsegrstSRS
(3.2):
8,3,
5)3,8(),,,(
]5[(...),(
22
2
2
232
2
stNDM
stMDM
substMNsubSRS
NstststSASAS
N
]5,3[3 NMst
(4):
)5()2.4()1.4(
),(...),...(
5,3,
)5,3(),,,(
...),),,,((
43
2
33
3
2
3
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
false
true
(4.1):
5,3,
)5,3((...),
33
3
2
stNDM
stMDM
falsegrstSRS
(4.2):
5,3,
2)3,5(),,,(
]2[(...),(
33
3
2
343
2
stNDM
stMDM
substMNsubSRS
NstststSASAS
N
]2,3[4 NMst
71
(5):
)6()2.5()1.5(
),(...),...(
2,3,
)2,3(),,,(
...),),,,((
54
2
44
4
2
4
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
true
true
(5.1): 2,3,
)2,3((...),
44
4
2
stNDM
stMDM
truegrstSRS
(5.2):
2,3,
1)2,3(),,,(
]1[(...),(
44
4
2
454
2
stNDM
stMDM
substNMsubSRS
MstststSASAS
N
]2,1[5 NMst
(6):
)7()2.6()1.6(
),(...),...(
2,1,
)2,1(),,,(
...),),,,((
65
2
55
5
2
5
2
ststSIFIF
stNDM
stMDM
trueneqstNMneqSRS
ststnMneqSWHWH
false
true
(6.1): 2,1,
)2,1((...),
55
5
2
stNDM
stMDM
falsegrstSRS
(6.2):
2,1,
1)1,2(),,,(
]1[(...),(
55
5
2
565
2
stNDM
stMDM
substMNsubSRS
NstststSASAS
N
]1,1[6 NMst
(7):
1,1,
)1,1(),,,(
...),),,,((
66
6
2
66
2
stNDM
stMDM
trueneqstNMneqSRS
stststnMneqSWHWH false
Результат: ]1,1[6 NMstst
72
Завдання 4.3. Нехай Sum_fac(n) – програма обчислення суми факторіалів до
вхідного n≥0 (включно) через множення і додавання.
Обчислити за натуральною семантикою результат застосування програми
Sum_fac(n) до даного [n->2].
Розв’язок.
Sum_fac(n) =
begin
i:=0;
fac:=1;
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
end
Введемо позначення:
Sum_fac(n) =
i:=0;
fac:=1;
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
P =
i:=0;
fac:=1;
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
P1 =
fac:=1;
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
P2 =
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
W =
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
73
Обчислення за натуральною семантикою:
]2[0 nst
)1.0(,
0,2
0,0
0,,0:
,
),(_
1
0
0110
0
0
inst
stNum
iststststiAS
ststPSEQ
ststnfacSumPR
)2.0(,
1,0,2
1,1
1,1:
,)1.0(
2
1
221
11
facinst
stNum
facstststfacAS
ststPSEQ
)3.0(,
1,1,0,2
1,1
1,1:
,)2.0(
3
2
23,32
22
resfacinst
stNum
resststststresAS
ststPSEQ
(0.3) = )2(),1(,
2,,0,
)2,0(,
,
33
3
3
stnVARstiVAR
trueneqstniB
ststWWH true
)1.1(,
1,1,1,2
1,1,0,
1)1,0(,1
1,,1:
,:;*:;1:
,:;*:;1:())1(
30
33
3
330303
43
43
resfacinst
stNumstiVar
addstiA
iststststiiAS
ststfacresresifacfaciiSEQ
ststendfacresresifacfaciibeginS
)3.1(),2.1(
,:;*:)1.1( 430 ststfacresresifacfac
SEQ
1,1,1,2
1,,1,
1)1,1(,*
1,,*:)2.1(
31
3030
30*
30313130
resfacinst
stiVarstfacVar
mulstifacA
facststststifacfacAS
74
2,1,1,2
1,,1,
2)1,1(,
2,,:)3.1(
4
3131
31
314431
resfacinst
stfacVarstresVar
addstfacresA
resststststfacresresAS
)4(),3(,2,,1,
)2,1(,
,)2(
44
4
4
stnVARstiVAR
trueneqstniB
ststWWH true
)1.3(,
2,1,2,2
1,1,1,
2)1,1(,1
2,,1:
,:;*:;1:
,:;*:;1:())3(
40
44
4
440404
54
54
resfacinst
stNumstiVar
addstiA
iststststiiAS
ststfacresresifacfaciiSEQ
ststendfacresresifacfaciibeginS
)3.3(),2.3(
,:;*:)1.3( 540 ststfacresresifacfac
SEQ
2,2,2,2
2,,1,
2)2,1(,*
2,,*:)2.3(
41
4040
40*
30414140
resfacinst
stiVarstfacVar
mulstifacA
facststststifacfacAS
4,2,2,2
2,,2,
2)2,2(,
4,,:)3.3(
5
4141
41
415541
resfacinst
stfacVarstresVar
addstfacresA
resststststfacresresAS
2,,2,
)2,2(,
,)4(
55
5
5
stnVARstiVAR
falseneqstniB
ststWWH false
Результат: 4,2,2,2 resfacinst
75
Тема 5. Аксіоматична семантика
Завдання 5.1. Довести часткову коректність програми DIV(a,b) (див. завдання
1.2) за допомогою логіки Хоара (в аксіоматичній семантиці).
Розв’язок.
Введемо позначення:
DIV(a,b) =
rez:=0; k:=0;
if a=0 then skip
else
if a<0 then skip
else
while k<=a-b do
begin rez:=rez+1;k:=k+b end;
ost:=a-k
S1 =
rez:=0;
k:=0;
S2 =
if a=0 then skip
else
if a<0 then skip
else
while k<=a-b do
begin rez:=rez+1;k:=k+b end;
ost:=a-k
IF1 =
if a=0 then skip
else
if a<0 then skip
else
while k<=a-b do
begin rez:=rez+1;k:=k+b end
IF2 =
if a<0 then skip
else
while k<=a-b do
begin rez:=rez+1;k:=k+b end
WH =
while k<=a-b do
begin rez:=rez+1;k:=k+b end
S3 =
rez:=rez+1;k:=k+b
Тоді маємо наступне виведення в логіці Хоара:
}{:}4{,
(*)
}4{1}2{
}{2}2{,
}2{0:}1{,
}1{0:}0{
}2{1}0{
}){,(}0{
qfkaostqAS
qIFqIF
qfSqSEQ
qkqAS
qrezqAS
qSqSEQ
qfbaDIVqSEQ
76
(*) =
(**),
}4{}4{
},4{}
,/{
}0,0,0
,0{}0&2{
}4{}0&2{
}4{2}0&2{,
}4{}4{
},4{}
,/{}0
,0,0,0
{}0&2{
}4{}0&2{
qskipq
qba
kbkrez
abk
rezaq
AX
qskipaqCONS
qIFaqIF
qskipq
qbak
bkrez
abk
rezaq
AX
qskipaqCONS
(**) =
*)*(*
},,/{
},/{
},,/{
}0,0,0
,0{}0&2{
}4{}0&2{
bakkabkrez
WHkabkrez
kabkrez
abk
rezaq
WH
qWHaqCONS
(***) =
},/{:}3{,
}3{1:}'3{
'3},,/{
}3{1:},,/{
},/{3},,/{
kabkrezbkkqAS
qrezrezq
qbakkabkrez
AS
qrezrezbakkabkrezCONS
kabkrezSbakkabkrezSEQ
де предикати мають такий вигляд:
}0{]0/[10 brezqq
}0,0{]0/[21 brezkqq
}0,0,0{2 bkrezq
},/{},1/1{
]1/}[,/)({]1/[3'3
bakbkrezbakbkrez
rezrezbkabbkrezrezrezqq
},/)({]/}[,/{3 bkabbkrezbkkkabkrezq
}0,,/{
]/}[0,,,/{]/[4
kabakbkrez
kaostostbakkaostbkrezkaostqfq
}0,,,/{ ostbakkaostbkrezqf
Інваріант цієї задачі для циклу while: P={rez=k/b}.
Розглянемо предикат qf: з нього випливає, що bkaost 0 . Далі, brezk * і
при цьому akba . Звідси випливає, що ]/[ barez , тобто скільки разів b
повністю укладається в a, або ж ціла частина від ділення, тоді
bbaabrezakaost *]/[* – залишок від ділення a на b за означенням.
77
Таким чином, з істинності 0b (предиката q0) як передумови випливає
істинність baostbarez mod],/[ (отриману з предиката qf еквівалентними
перетвореннями) як післяумови, і це доводить часткову коректність програми.
Завдання 5.2.
1) Побудувати програму обчислення n! через операцію «+».
2) Довести часткову коректність побудованої програми за допомогою логіки
Хоара (в аксіоматичній семантиці).
Розв’язок.
1) Програма обчислення n! через операцію додавання:
Prog(n) =
begin
res := 1;
i : = 1;
while i <= n do
begin
j := 1;
k := res;
while j < i do
begin
k := k + res;
j := j+1
end;
res := k;
i := i + 1
end
end
На тексті програми введено позначення (скорочення), які ми будемо
використовувати далі.
2) Доведення часткової коректності.
Деякі предикати, які зустрінуться в доведенні:
)0!(
)10)!1((7
nnresqf
niniresq
Необхідно показати, що в результаті обчислень за програмою ми отримаємо
!nres , що еквівалентно qf.
S2
F6
WH(…)2 F5
F4
F3 або
WH(…)1
F2
78
Решту предикатів ми будемо обчислювати в ході виведення (доведення) в
логіці Хоара:
)1(
}12{1:}1{
)10(
)1210(1
}2{1:}1{
}{2}1{
)0()110(
}1{1:}{
}0!{}{
iqiq
resn
resnnq
CONS
qiqAS
qfFqSEQ
nnT
qresTAS
nnresrogPTSEQ
(1)
)2(
)01)011(()01(
}3{1:}01{
}3{1:}20)!1(1{
}120)!1({4
}20)!1(1{
}20)!1({1)(}0)!1(21{
}{3}2{
niresni
qjni
qjniniresnii
ininiresF
niniresnii
SEQ
niniresniWHniresniiWH
qfFqCONS
(2)
)3(
)0()01(
)0(3
}4{:}3{
}20)!1({5}3{
}120)!1({5}3{
nijresj
nijjresresq
qreskqAS
nininiresFqSEQ
ininiresFqCONS
(3)
)6(
}7{6}5{
)4(
}5{2)(}4{
}7{1:;:;2)(}4{
qFqSEQ
qWHq
qiikresWHqSEQ
(4)
)5(
))(0(
)))1((0(
}12{:})(0{
}0{2})(0{
}0{2)(}0{
ijjreskn
ijjresreskn
qreskkijijjresknAS
ijnjreskSijijjresknSEQ
njreskijijWHnijjreskWH
(5)
))0()()1(()01)1((12
}0{1:}12{
nijjresknijjreskq
nijjreskjjq
79
(6)
)0!(6
}10)!1({1:}6{
)0!(5
}6{:}5{
niniresq
niniresiiqAS
ninikq
qkresqAS
Таким чином, з істинності 0n (предиката T) як передумови випливає
істинність !nres (предиката qf) як післяумови, що доводить часткову
коректність програми.
Завдання 5.3. Довести часткову коректність програми Sum_fac(n) обчислення
суми факторіалів до вхідного n≥0 (включно) через множення і додавання (див.
завдання 4.3) за допомогою логіки Хоара (в аксіоматичній семантиці).
Розв’язок.
Введемо позначення:
Sum_fac(n) =
i:=0;
fac:=1;
res:=1;
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac
end
W =
while i ≠ n
begin
i:=i+1;
fac:= fac*i;
res:=res+fac;
end
Позначення предикатів:
P = 0}{n
P0 = 1}res1,fac0,0,{ in
P01 = 0}i0,{n
P02 = 1}fac0,i0,{n
T = }k!res,i!{faci
0k
T0 = }k!res,1)!-i({fac1-i
0k
T1 = }k!res,i!{fac1-i
0k
B = }{ ni
80
)1(,
}{}W {,
)0(,}00 0,n{
}0{:}i{
}{ 1;:res 1;:fac 1;:}i{
}um_fac(n){ }{
0
0
01
0
TP
BTPCONS
P
PPAS
PPSEQ
BTSPSEQ
}11,1
,0,0{
}{1:}{,
}11
,0,0{
}{1:}{
}{0:;1:}{)0(
02
002
01
0201
001
fac
inP
PresPAS
inP
PfacPAS
PresfacPSEQ
(2),k!res,)!11({T
}1{Ti:B}i{T
fac{T}res:resi;*fac :fac1;i:i}{
}{}{)1(
1-1i
0k
0
ifac
BAS
BTSEQ
BTWTWH
(3),}k!res,i!i*fac{T
B}{Ti;*fac :}fac{T
fac{T}res:resi;*fac :}fac{TSEQ (2)
1-i
0k0
10
0
BAS
B
}k!,!{
}{:}{,TBT
fac{T}res:B}res{T)3(
i
0k1
111
1
facresifacT
TfacresresTAS
Cons
Таким чином, з істинності 0n (предиката P) як передумови випливає
істинність
n
0kk!res (як предиката BT , тобто }k!res,i!{fac
i
0k та
{i = n}) як післяумови, що і доводить часткову коректність програми.
81
Література
1. Nikitchenko N. A Composition Nominative Approach to Program Semantics. –
Technical Report IT-TR: 1998-020. – Technical University of Denmark. –
1998. – 103 p.
2. Басараб И.А., Никитченко Н.С., Редько В.Н. Композиционные базы
данных. – К.: Либідь, 1992. – 191 с.
3. Winskel G. The Formal Semantics of Programming Languages: An
Introduction. – London: MIT Press Foundations of Computing Series, 1993. –
361 p.
4. Nielson H.R., Nielson F. Semantics with Applications: A Formal Introduction.
– Wiley Professional Computing, 1992. – 240 p.
5. Редько В.Н. Семантические структуры программ // Программирование. –
1981. – № 1. – С. 3–19.
6. Никитченко Н.С. Композиционная семантика языков программирования
// Программирование. – 1982. – № 6. – С. 9–18.
7. Нікітченко М. С., Панченко Т. В. Структури даних в композиційних
мовах програмування //Вісник Київського університету. Серія: фіз.-мат.
науки. – 2004. – вип. 2. – С. 316–325.
8. Панченко Т. В. Моделювання структур даних та функцій над ними в
композиційно-номінативній мові ACoN //Проблемы программирования. –
2004. – №1-2. – С. 7–15.
9. Панченко Т. В. Композиційні методи специфікації та верифікації
програмних систем. Дисертація на здобуття наукового ступеня кандидата
фізико-математичних наук. – Київ, 2006. – 177 с.