Денотационная семантика <двоичное_число> → 0 | 1 |<двоичное_число>0 |<двоичное_число>1 Mb: Мb('0') = 0, Мb('1')=1 Мb(<двоичное_число> '0') = =2 * Мb(<двоичное_число>) Мb(<двоичное_число> ‘1’) = =2 * Мb(<двоичное_число>) + 1 <десятичное_число> → 0|1|2|3|4|5|6|7|8|9 | <десятичное_число> (0|1|2|3|4|5|6|7|8|9) Синтаксические правила: Md(‘0’) = 0, Md('1') = 1, ,..., Md('9') = 9 Мd(<десятичное_число> '0') = =10 * Мd(<десятичное_число>) Мd(<десятичное_число> ‘1’) = = 10 * Мd(<десятичное_число>) + 1 … Мd(<десятичное_число> '9') = = 10 * Мd(<десятичное_число>) + 9 s {<i1, v1>, <i2, v2>, …, <in, vn>} VARMAP(ik, s) == vk <выражение> → <десятичное_число> | <переменная> | <двоичное_выражение> <двоичное_выражение> → <выражение_слева><оператор><выражение_справа> <оператор> → + | * Me (<выражение>,s) case <выражение> of <десятичное_число> => Md(<десятичное_число>, s) < переменная> => if VARMAP(<переменная>, s) = undef then error else VARMAP(<переменная>, s) <двоичное_выражение> => if(Me(<двоичное_выражение>.<выражение_слева>, s) = undef OR Me(<двоичное_выражение>.< выражение_справа >, s) = undef) then error else if(<двоичное_выражение>.< оператор > = '+' then Me(<двоичное_выражение>.<выражение_слева>, s) + Me(<двоичное_выражение>.<выражение_справа>, s) else Me(<двоичное_выражение>.<выражение_слева>, s) * Me(<двоичное_выражение>.<выражение_справа>, s) Ма(х = Е, s) if Me(E, s) = error then error else s' = {< i1', v1' >, < i2', v2' >,..., < in', vn' >} where for (j = 1, 2, ..., n) if ij <>x then vj’ = VARMAP(ij’, s); if ij = x then vj’ = Me(E, s) Аксиоматическая система Хоара {P} A {Q} Законы консеквенции: {P} A {Q}, Q=>V |- {P} A {V} {P} A {Q}, W=>P |- {W} A {Q} Алгебраические типы данных push: pop: top: empty: size: newstack: stackintegerstack stackstack stackinteger {undefined} stackboolean stackinteger stack Операции, определяющие тип данных: 1. Генераторы (g: not_xx) 2. Конструкторы (c: x not_xx) 3. Функции push(push(push(newstack, 1), 5), 3) 1. pop(newstack)= newstack 2. pop(push(S, I))= S 3. top(newstack)= undefined 4. top(push(S,I))= I 5. empty(newstack)= true 6. empty(push(S,I))= false 7. size(newstack)= 0 8. size(push(S,I))= size(S)+1 Пример: empty(pop(push(push(newstack, 42), 17))) А2: empty(push(newstack, 42)) A6: false Индукция типов данных 1. x, gi, ci, fi, P(y) yx. 2. P(gi)=true 3. P(y)=true => P(ci(y))=true. 4. P(y)=true, yx. Пример: P(newstack), P(push(x, i)) size(push(S,x))>size(S) – исх. утверждение Доказательство: size(S)+1>size(S) (A8) newstack: push(S, x): 0+1>0 size(newstack)+1>size(newstack) (A7) size(S)+1>size(S) - гипотеза size(push(S,x))+1>size(push(S,x)) (A8) size(S)+1+1>size(S)+1 Методы спецификации программ Виды спецификаций: 1. Языки спецификации задач • Языки описания требований; • Языки функциональных спецификаций. 2. Языки спецификации свойств. Языки описания требований: RSL, SDL,PSL. value fact : Nat -~-> Nat fact(n) is if n = 1 then 1 else n * fact(n-1) end pre n > 0 Языки функциональных спецификаций SETL, SPECIAL, CLEAR, RDM, CIP-L, META-IV, Jota. Качества функциональных спецификаций: • однозначность (точность); • понятность (ясность); • полнота описания задачи. Свойства языков функциональных спецификаций: • выразительность; • выполнимость спецификаций; • доказуемость. Методы верификации программ Свойства корректности программ: 1. Частичная корректность 2. Завершение Суть метода индуктивных утверждений: • формулируются входное и выходное утверждения; • строится промежуточное утверждение; • формулируется теорема (условия верификации): из выведенного утверждения следует выходное утверждение; • доказывается теорема. Алгоритм доказательства правильности программ: 1. Построить структуру программы. 2. Выписать входное и выходное утверждения. 3. Сформулировать для всех циклов индуктивные утверждения. 4. Составить список выделенных путей. 5. Построить условия верификации. 6. Доказать условия верификации. 7. Доказать, что выполнение программы закончится.