Форум МГОУ Прокопьевск

Объявление

Кому нужна квартира на сутки ночь час обрашяйтесь к TRojan

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Форум МГОУ Прокопьевск » Московский Государственный Открытый Университет » теория вычислительных процессов


теория вычислительных процессов

Сообщений 1 страница 2 из 2

1

ТЕОРИЯ ВЫЧИСЛИТЕЛЬНЫХ ПРОЦЕССОВ
50. Формальные языки и грамматики. Классификация грамматик по Хомскому. Контекстно-свободные грамматики и их свойства. Распознаватели. Конечные автоматы.
Понятия грамматика, словарь, цепочка, язык.
Мы неформально определяем язык как подмножество множеств всех предложений из "слов" или символов некоторого основного словаря. Нас не интересует смысл этих предложений. Например, русский язык состоит из предложений, которые являются последовательностями, составленными из слов (Пусть, всегда, будет, солнце и т.д) и знаков пунктуации (например, запятые, точки, скобки). Язык программирования Паскаль состоит из программ, которые являются последовательностями, составленными из таких символов, как begin, end, знаков пунктуации, букв и цифр. Язык четных чисел состоит из последовательностей, составленных из цифр 1, ..., 9, в которых последней цифрой должны быть О, 2, 4, 6 или 8.
Алфавит - это непустое конечное множество элементов. Назовём элементы алфавита символами. Всякая конечная последовательность символов алфавита А называется цепочкой. Вот несколько цепочек "в алфавите" А={ а, b, c}: а, b, с, ab и ааса. Мы также допускаем существование пустой цепочки , т. е. цепочки, не содержащей ни одного символа. Важен порядок символов в цепочке; так цепочка ab не то же самое, что ba, и abca отличается от aabc. Длина цепочки х (записывается как |х|) равна числу символов в цепочке. Таким образом, | |=0, |a|=1, |abb|=3.
Заглавные буквы М, N, S, Т, U, используются как переменные или имена символов алфавита, в то время как строчные буквы u, v, w используются для обозначения цепочек символов. Таким образом, можно написать x=STV, и это означает, что х является цепочкой, состоящей из символов S, Т и V именно в таком порядке.
( Вместо термина цепочка (в английском языке - string) некоторые авторы используют термины строка или строчка. )
Если x и y - цепочки, то их катенацией xy является цепочка , полученная путем дописывания символов цепочки y вслед за символами цепочки x. Например, если x = XY, y = YZ, то xy = XYYZ и yx = YZXY. Поскольку Λ, не содержащая символов, то в соответствии с правилом катенации для любой цепочки x мы можем записать:
Λx = xΛ = x.
Если z = xy - цепочка, то x - голова, а y - хвост цепочки z. x - правильная голова, если y - не пустая цепочка, z - правильный хвост, если x - не пустая цепочка. Множества цепочек в алфавите обычно обозначаются заглавными буквами A, B . . . Произведение AB двух множеств цепочек A и B определяется как
AB = {xy | x  A, y  B}
и читается как "множество цепочек xy такое, что x из A, а y из B". Например, если A = {a, b}, B ={c, d} , то множество AB={ac, ad, bc, bd}. Поскольку Λx = xΛ =x справедливо для любой цепочки x, мы имеем
{Λ} A = A {Λ } =A.
Заметьте, что здесь символ Λ заключен в фигурные скобки. Произведение определено для множеств, тогда как Λ символом. {Λ } - это множество, состоящее из пустого символа Λ.
Мы можем определить степени цепочек. Если x - цепочка, то x0 - пустая цепочка , x1 = x, x2 = xx, и в общем случае
xn = x....x (n раз).
Для n>0 имеем xn = xxn-1 = ( xn-1 )x.
Так же можно определить степени алфавита A:
для n > 0:
A0 = { Λ }; A1 = A; An = AAn-1.
Используя это, определим две последние операции в этом разделе - итерацию A* множества A и усеченную итерацию A+ множества A:
A+ = A1  A2  ...  An  ...,
A* = A0  A+ .
Таким образом, если А={а, b}, то А* включает цепочки Λ, а, b, аа, аb, bа, bb, ааа, ааb...
Заметим, что А+=АА*=(А*)А.
Иногда удобнее и, как правило, нагляднее писать х... вместо ху, если нас не интересует у - остальная часть цепочки. Таким образом, три точки "..." обозначают любую возможную цепочку, включая и пустую. Наиболее часто встречаются следующие обозначения:
Обозначение Смысл
z = х ...    x - голова цепочки z, нам безразличен хвост.
z = ...х    x - хвост цепочки z. нам безразлична голова.
z = ...x...     х встречается где-то в цепочке z.
z = S...    Символ S-первый символ цепочки z.
z = ...S    Символ S - последний символ цепочки z.
z = ...S...    Символ S встречается где-то в цепочке z.
Грамматики.
Определение. Грамматикой G[Z] называется конечное, непустое множество правил; Z - это символ, который должен встретиться в левой части по крайней мере одного правила. Он называется начальным символом . Все символы, которые встречаются в левых и правых частях правил, образуют словарь V.
Если из контекста ясно, какой символ является начальным символом Z, мы будем писать G вместо G[Z].
0пpeдeлeние. В заданной грамматике G символы, которые встречаются в левой части правил, называются нетерминалами или синтаксическими единицами, языка. Они образуют множество нетерминальных символов VN.
Символы, которые не входят в множество VN, называются терминальными символами (или терминалами). Они образуют множество VT.
Таким образом, V=VN  VT. Как правило, нетерминалы мы будем заключать в угловые скобки <и>, чтобы отличить их от терминалов. В грамматике G1 символы 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - терминальные, а <число>, <чс> и <цифра> - нетерминальные. Мы будем пользоваться надстрочными литерами в том случае, когда нам надо отличить различные вхождения одного и того же нетерминала. Так, можно написать <чс1 >: := <чс2 > <цифра> вместо <чс>::=<чс><цифра>.
Множество правил U::=х, U::=у,..., U::=z с одинаковыми левыми частями будем записывать сокращенно как U : :=х|у|...|z.
Определение языка, соответствующего грамматике.
Что является предложением этого языка? Для того чтобы ответить на эти вопросы, нам надо определить символы "=>" и "=>+", которыми мы интуитивно пользовались при выводе предложений. Неформально мы пишем v=>w, если можно вывести w из v, заменив нетерминальный символ в v на соответствующую правую часть некоторого правила.
0пределение. Пусть G - грамматика. Мы говорим, что цепочка v непосредственно порождает цепочку w, и обозначаем это как
v=>w,
если для некоторых цепочек х и у можно написать
v=xUy, w=xuy,
где U ::= u - правило грамматики G. Мы также говорим, что w непосредственно выводима из v или что w непосредственно приводится (редуцируется) к v. Цепочки x и у могут, конечно, быть пустыми. Следовательно, для любого правила U ::= u грамматики G имеет место U=>u. Чтобы задать грамматику G=(E,Sym,P), требуется указать:
 множество символов алфавита (или терминальных символов) E. Будем обозначать их строчными символами алфавита и цифрами;
 множество нетерминальных символов Sym (или метасимволов), не пересекающееся с E со специально выделенным начальным символом S. Будем обозначать их прописными буквами;
 множество правил вывода P определяющих правила подстановки для цепочек. Каждое правило состоит из двух цепочек (например, x и y), причем x должна содержать по крайней мере один нетерминал; и означает, что цепочку x в процессе вывода можно заменить на y. Вывод цепочек языка начинается с нетерминала S. Правило грамматики будем записывать в виде x : y. (Также употребляется запись x ::= y или x -> y)
Более строго, определим понятие выводимой цепочки:
 S - выводимая цепочка;
 если xyz - выводимая цепочка и в грамматике имеется правило y:t, то xtz - выводимая цепочка;
 определяемый грамматикой язык состоит из выводимых цепочек, содержащих только терминальные символы.
Для сокращения записи принято использовать символ "или" - "|".
Классификация языков по Хомскому
Хомский определил четыре основных класса языков в терминах грамматик, являющихся упорядоченной четверкой (V, Т, Р, Z), где
1. V - алфавит;
2. Т  V - алфавит терминальных символов;
3. Р - конечный набор правил подстановки;
4. Z - начальный символ, принадлежащий множеству V - Т.
Язык, порождаемый грамматикой, - это множество терминальных цепочек, которые можно вывести из Z. Различие четырех типов грамматик заключается в форме правил подстановки, допустимых в Р. Говорят, что G - это (по Хомскому) грамматика типа 0 или грамматика с фразовой структурой, если правила имеют вид
u:: = v, где u V и v V*. (5.1)
То есть левая часть u может быть тоже последовательностью символов, а правая часть может быть пустой. Грамматикам с фразовой структурой посвящено сравнительно немного работ. Если ввести ограничение на правила подстановки, то получится более интересный класс языков типа 1, называемых также контекстно-чувствительными или контекстно-зависимыми языками. В этом случае правила подстановки имеют следующий вид:
xUy::= хuу, где U  V - Т, u V+ и x,у V*. (5.2)
Термин "контекстно-чувствительная" отражает тот факт, что можно заменить U на u лишь в контексте х...у. Дальнейшее ограничение дает класс грамматик, полностью подобный классу, который мы используем; грамматика называется контекстно-свободной, если все ее правила, имеют вид
U::=u, где U  V-T и u V*. (5.3)
Этот класс назван контекстно-свободным потому, что символ U можно заменить цепочкой u, не обращая внимания на контекст, в котором он встретился. В КС-грамматике может появиться правило вида U := Λ, где Λ - пустая цепочка. Однако, чтобы не усложнять терминологию и доказательства, мы не допускаем таких правил в наших грамматиках. По заданной КС-грамматике G можно сконструировать Λ-свободную (или неукорачивающую) грамматику G1 (наш тип), такую, что L(G1) = L(G) - {Λ }. Более того, если G однозначна, то G1 также однозначна, поэтому фактически мы не вносим ограничений.
Если мы ограничим правила еще раз, приведя их к виду
U ::= N или U ::= WN, где N Т, а U и W  V - T, (5.4)
то получим грамматику типа 3, или регулярную грамматику. Регулярные грамматики играют основную роль как в теории языков, так и в теории автоматов. Множество цепочек, порождаемых регулярной грамматикой, "допускается" машиной, называемой автоматом с конечным числом состояний (которую мы определим в следующей главе), и наоборот. Таким образом, мы имеем характеристику этого класса грамматик в терминах автомата. Регулярные языки (те, что порождаются регулярными грамматиками), кроме того, называются регулярными множествами. Известно, что если L1 и L2 - регулярные множества, то таковыми же являются L1  L2, L1 ∩ L2, L1 - L2, L1•L2 = {xy | x  L1 и у L2} и L1* ={Λ}  L1  L12  L13  ... .
Вводя все большие ограничения, мы определили четыре класса грамматик. Таким образом, есть языки с фразовой структурой, которые не являются контекстно-чувствительными, контекстно-чувствительные языки, которые не являются контекстно-свободными, и контекстно-свободные, которые не являются регулярными.
В большинстве работ по теории формальных языков изучаются контекстно-свободные или регулярные языки, или их подмножества. Поэтому мы также ограничимся этими классами.
Один из основных вопросов, который возникает при рассмотрении грамматики, - это вопрос ее однозначности. К сожалению (или к счастью, в зависимости от того, как вы на это смотрите), было доказано, что эта проблема алгоритмически неразрешима для класса КС-грамматик. Это означает, что нельзя построить эффективный алгоритм (написать программу), который для любой заданной грамматики за конечное число шагов мог бы решить, является она однозначной или нет. Оказываются алгоритмически неразрешимыми и многие другие интересные вопросы, касающиеся контекстно-свободных языков (например, совпадают ли два языка, пересекаются ли два языка). В этом случае ищут интересные подклассы языков, для которых этот вопрос алгоритмически разрешим. Большинство доказательств теорем алгоритмической неразрешимости в теории формальных языков прямо или косвенно зависит от результатов теоремы Поста.
Доказано, что проблема однозначности для произвольной КС- грамматики алгоритмически неразрешима. Следующий вопрос, который может возникнуть: всегда ли существует однозначная грамматика для произвольного контекстно-свободного языка? Ответ отрицательный. Есть языки, для которых не существуют однозначные грамматики; первым это показал Парик. Такие языки называются существенно-неоднозначными. Пример такого языка: {ai bi cj | i, j ≥ 1} u {ai bj cj | i, j ≥ 1}.
Распознаватели.
Сканер представляет собой ту часть компилятора, которая читает литеры первоначальной исходной программы и строит слова, или иначе символы, исходной программы (идентификаторы, служебные слова, целые числа, одно- или двулитерные разделители, такие, как *, +, **, /*). Иногда символы называют атомами, или лексемами. В чистом виде сканер выполняет простой лексический анализ исходной программы в отличие от синтаксического анализа, и поэтому сканер называют также лексическим анализатором.
Сразу же возникает вопрос, почему лексический анализ нельзя объединить с синтаксическим анализом. В конце концов для описания синтаксиса символов можно воспользоваться БНФ. Например, в ФОРТРАНе идентификатор можно описать следующим образом:
<идентификатор> : : = буква {буква | цифра}
Однако есть несколько серьезных доводов в пользу отделения лексического анализа от синтаксического.
1. Значительная часть времени компиляции тратится на сканирование литер. Выделение же позволяет нам сконцентрировать внимание на сокращении этого времени. Одним из способов сокращения времени является программирование части или всего сканера на автокоде, а это сделать легче, если сканер выделен. Конечно же, мы не рекомендуем пользоваться автокодом, если не предполагается широкое и массовое применение компилятора.
2. Синтаксис символов можно описать в рамках очень простых грамматик. Если отделить сканирование от синтаксического распознавания, то можно разработать эффективную технику разбора, которая наилучшим образом учитывает особенности этих грамматик. Более того, тогда для конструирования сканеров можно разработать автоматические методы, в которых используется эта эффективная техника.
3. Так как сканер выдает символы вместо литер, синтаксический анализ на каждом шаге получает больше информации о том, что надо делать. Более того, некоторые специфические проверки контекста, необходимые при разборе символов, проще и уместнее выполнить в сканере, чем в формальном синтаксическом анализаторе. Например, легко выяснить, что означает в ФОРТРАНе инструкция D010I = ... , установив, какой из символов "," или "(" встречается раньше после знака равенства.
4. Развитие языков высокого уровня требует внимательного отношения как к лексическим, так и к синтаксическим свойствам языка. Разделение этих двух свойств позволит нам исследовать их независимо друг от друга.
5. Часто для одного и того же языка существует несколько различных внешних представлений. Например, в некоторых реализациях АЛГОЛа служебные слова заключены в кавычки и пробелы не играют никакой роли - они попросту игнорируются. В других же компиляторах служебные слова не могут использоваться как идентификаторы и смежные служебные слова и идентификаторы должны отделяться друг от друга по крайней мере одним пробелом. Внешние представления на перфоленте, картах и на телетайпе могут быть совершенно различными.
Выделение сканера позволит написать один синтаксический анализатор и несколько сканеров (которые написать проще и легче) по одному на каждое представление исходной программы и/или устройство ввода. При этом каждый сканер переводит символы в одинаковую внутреннюю форму, используемую синтаксическим анализатором.
Сканер можно запрограммировать как отдельный проход, на котором выполняется полный лексический анализ исходной программы и который выдает синтаксическому анализатору таблицу, содержащую исходную программу в форме внутренних символов. В другом варианте это может быть подпрограмма SCAN, к которой обращается синтаксический анализатор всякий раз, когда ему необходим новый символ. В ответ на каждый вызов SCAN распознает следующий символ исходной программы и отсылает его синтаксическому анализатору. Последний вариант, вообще говоря, лучше, поскольку нет необходимости конструировать целиком всю внутреннюю исходную программу и хранить ее в таком виде в памяти. Далее мы будем предполагать, что сканер должен быть реализован именно таким способом.
Будем считать, что исходная программа пишется в формате, в котором границы полей не фиксируются, т. е. в виде непрерывной последовательности литер.
Иногда остается неясным различие между символами и конструкциями более высокого уровня. Например, можно рассматривать в качестве символов как целое, так и вещественное число вида <целое> . <целое> или же можно рассматривать целое число как символ, а вещественное - как конструкцию более высокого уровня. В таких случаях мы будем делать произвольный выбор главным образом в интересах простоты изложения.
Конечный автомат – система объектов  , в которой  ,  ,  , Q, T, V – конечные множества (алфавиты), Q - алфавит состояний, Т - входной алфавит, V - выходной алфавит,   - функция переходов (определяется как отображение множества   в множество  , т.е.  ),   - функция выходов  , т.е. отображается на множестве V.
Упрощенная схема конечного автомата.
Состояние автомата отождествляется с состоянием внутренней памяти. Внутреннее состояние зависит от состояния входа в данный момент и от внутреннего состояния в предыдущий момент времени. Состояние автомата задается совокупностью внутреннего состояния и состояния входа.
Функция перехода   описывает смену внутреннего состояния автомата, т.е. изменение содержимого внутренней памяти в зависимости от того, что в ней хранилось до изменения входного символа и от того, как изменилось состояние входа. Т.е. это функция двух аргументов:  .
Значение   зависит от   - это состояние, в котором окажется автомат после замены входного сигнала ai на aj, если он начинает работу, находясь в состоянии qi.
Если функция   по данному текущему состоянию к текущему входному символу указывает множество возможных следующих состояний, то автомат называется недетерминированным, т.е.

Если множество   содержит не более одного состояния для абсолютно любого q и a, то автомат называется недетерминированным.
Функция выходов   описывает изменение выходных символов автомата под действием входных символов и в зависимости от состояния внутренней памяти автомата  .
Значение   - тот символ, который установится на выходе автомата после замены входного символа ai на aj и перехода автомата из состояния qi к qk.

Если для некоторого состояния   значения   и   (или одного из них) не определены, то автомат V частичным (не полностью определенным).
Если множества   и   содержат точно по одному элементу для любых q, a, V, то автомат называется полным.
Способы задания конечных автоматов.
Q T
a1 a2 a3
q1 q3, V1 q3, V2 q2, V1
q2 q4, V1 q1, V1 q1, V1
q3 q2, V1 q3, V1 q3, V2
q4 q4, V1 q2, V1 q1, V2
I. Конечный способ.

Столбцы таблицы соответствуют различным входам автомата. Строки соответствуют состояниям автомата. В любой клетке таблицы записываются значения следующего состояния автомата и значение выходного сигнала, соответствующего следующему состоянию qk и Ve.
II. Граф перехода.
Вершины помечаются номерами состояний автомата. Дуги, соединяющие вершины, помечаются входным символом, который вызвал переход автомата из I-го состояния в j-тое, а также выходным символом, который устанавливается на выходе автомата в результате этого перехода.
Пусть автомат находится в состоянии q3, a1 – это устойчивое состояние. При замене а1 на а2 автомат перейдет в состояние q2, не задержится и перейдет в q1, затем в q3.
Детерминированным называется автомат, граф перехода которого не содержит вершин, имеющих одинаково помеченные дуги.
III. Матрица переходов.
Это точная копия графа перехода. Строки и столбцы матрицы переходов соответствуют записям над дугами соответствующих вершин.

0

2

53. Верификация программ. Методы доказательства правильности программ. Правила верификации К.Хоара.
Верификация - это процесс определения, выполняют ли программные средства и их компоненты требования, наложенные на них в последовательных этапах жизненного цикла разрабатываемой программной системы.
Основная цель верификации состоит в подтверждении того, что программное обеспечение соответствует требованиям. Дополнительной целью является выявление и регистрация дефектов и ошибок, которые внесены во время разработки или модификации программы.
Верификация является неотъемлемой частью работ при коллективной разработке программных систем. При этом в задачи верификации включается контроль результатов одних разработчиков при передаче их в качестве исходных данных другим разработчикам.
Для повышения эффективности использования человеческих ресурсов при разработке верификация должна быть тесно интегрирована с процессами проектирования, разработки и сопровождения программной системы.
Заранее разграничим понятия верификации и отладки. Оба этих процесса направлены на уменьшение ошибок в конечном программном продукте, однако отладка - процесс, направленный на локализацию и устранение ошибок в системе, а верификация - процесс, направленный на демонстрацию наличия ошибок и условий их возникновения.
Кроме того, верификация, в отличие от отладки - контролируемый и управляемый процесс. Верификация включает в себя анализ причин возникновения ошибок и последствий, которые вызовет их исправление, планирование процессов поиска ошибок и их исправления, оценку полученных результатов. Все это позволяет говорить о верификации как о процессе обеспечения заранее заданного уровня качества создаваемой программной системы.
Методы доказательства правильности программ.
К неформальным методам доказательства правильности программ относят отладку и тестирование, которые являются необходимой составляющей на всех этапах процесса программирования, хотя и не решают полностью проблемы правильности. Существенные ошибки легко найти, если использовать приемы отладки (контрольные распечатки, трассировки).
Тестирование - процесс выполнения программы с намерением найти ошибку, а не подтвердить правильность программы. Тестированию и отладке посвящено много работ как отечественных, так и зарубежных.
Метод установления правильности программ при помощи строгих средств известен как верификация программ.
В отличие от тестирования программ, где анализируются свойства отдельных процессов выполнения программы, верификация имеет дело со свойствами программ.
В основе метода верификации лежит предположение о том, что существует программная документация, соответствие которой требуется доказать. Документация должна содержать следующие спецификации:
• спецификация ввода-вывода (описание данных, не зависящих от процесса обработки);
• свойства отношений между элементами векторов состояний в выбранных точках программы;
• спецификации и свойства структурных подкомпонентов программы;
• спецификация структур данных, зависящих от процесса обработки.
К такому методу доказательства правильности программ относится
Метод индуктивных утверждений, независимо сформулированный К. Флойдом и П. Науром. Суть этого метода состоит в следующем:
1) формулируются входное и выходное утверждения: входное утверждение описывает все необходимые входные условия для программы (или программного фрагмента), выходное утверждение описывает ожидаемый результат;
2) предполагая истинным входное утверждение, строится промежуточное утверждение, которое выводится на основании семантики операторов, расположенных между входом и выходом (входным и выходным утверждениями); такое утверждение называется выведенным утверждением;
3) формулируется теорема (условия верификации): из выведенного утверждения следует выходное утверждение;
4) доказывается теорема; доказательство свидетельствует о правильности программы (программного фрагмента).
Доказательство проводится при помощи хорошо разработанных математических методов, использующих исчисление предикатов первого порядка.
Условия верификации можно построить и в обратном направлении, т.е., считая истинным выходное утверждение, получить входное утверждение и доказывать теорему:
из входного утверждения следует выведенное утверждение.
Такой метод построения условий верификации моделирует выполнение программы в обратном направлении. Другими словами, условия верификации должны отвечать на такой вопрос: если некоторое утверждение истинно после выполнения оператора программы,то какое утверждение должно быть истинным перед оператором?
Определение индуктивных утверждений помогает формализовать интуитивные представления о логике программы.
Самым сложным в процессе доказательства является построение индуктивных утверждений. Это объясняется, во-первых,тем, что необходимо описать все содержательные условия, и, во-вторых, тем, что необходимо аксиоматическое описание семантики языка программирования.
Важным шагом в процессе доказательства является доказательство завершения выполнения программы, для чего бывает достаточно неформальных рассуждений.
Таким образом, алгоритм доказательства правильности программы методом индуктивных утверждений представляется в следующем виде.

Описание алгоритма.
1) Построить структуру программы.
2) Выписать входное и выходное утверждения.
3) Сформулировать для всех циклов индуктивные утверждения.
4) Составить список выделенных путей.
5) Построить условия верификации.
6) Доказать условие верификации.
7) Доказать, что выполнение программы закончится.
Этот метод сравним с обычным процессом чтения текста программы (метод сквозного контроля). Различие заключается в степени формализации.
Преимущество верификации состоит в том, что процесс доказательства настолько формализуем, что он может выполняться на вычислительной машине. В этом направлении в восьмидесятые годы проводились исследования, даже создавались автоматизированные диалоговые системы, но они не нашли практического применения.
Для автоматизированной диалоговой системы программист должен задать индуктивные утверждения на языке исчисления предикатов. Синтаксис и семантика языка программирования должны храниться в системе в виде аксиом на языке исчисления предикатов. Система должна определять пути в программе и строить условия верификации.
Основной компонент доказывающей системы - это построитель условий верификации, содержащий операции манипулирования предикатами, алгоритмы интерпретации операторов программы. Вторым компонентом системы является подсистема доказательства теорем, что относится к области искусственного интеллекта.
Отметим трудности, связанные с методом индуктивных утверждений. Трудно построить "множество основных аксиом, достаточно ограниченное для того, чтобы избежать противоречий, но достаточно богатое для того, чтобы служить отправной точкой для доказательства утверждений о программах" (Э. Дейкстра). Вторая трудность - семантическая, заключающаяся в формировании самих утверждений, подлежащих доказательству. Если задача, для которой пишется программа, не имеет строгого математического описания, то для нее сложнее сформулировать условия верификации.
Перечисленные методы имеют одно общее свойство: они рассматривают программу как уже существующий объект и затем доказывают ее правильность.
Метод, который сформулировали К. Хоар и Э. Дейкстра и которому посвящено дальнейшее изложение, основан на формальном выводе программ из математической постановки задачи.
Э. Дейкстра предложил исчисление программ — дисциплину, включающую набор правил для вывода программ. Применение этих правил дает последовательность логических выводов, заканчивающуюся получением правильной программы для некоторой задачи. Этот подход требует конструктивной правильности при разработке программы. Значит, программа правильна по построению, так что последующих доказательств ее правильности не нужно.
Правила верификации К.Хоара.
Основой для исчисления выводов программ служат правила К.Хоара (правила верификации) для интерпретации программных конструкций. Сформулируем правила (аксиомы) К.Хоара, которые определяют предусловия как достаточные предусловия, гарантирующие, что исполнение соответствующего оператора при успешном завершении приведет к желаемым постусловиям.
  A1. Аксиома присваивания:  { Ro }    x := e   { R }
Неформальное объяснение аксиомы: так как x после выполнения будет содержать значение e, то R будет истинно после выполнения, если результат подстановки e вместо x в R истинен перед выполнением.
Таким образом Ro = R(x) при x = e.
Для Ro вводится обозначение: Ro=Rxe   (у Вирта)   или   Rx->e   (у Дейкстры)
что означает, что x заменяется на e.
Аксиома присваивания будет иметь вид:
{ Rxe}   x:=e   {R}
Сформулируем два очевидных правила.
A2. Если известно: { Q } S { P } и { P } => { R }, то { Q } S { R }
A3. Если известно: { Q } S { P } и { R } => { Q }, то { R } S { P }
Пусть S - это последовательность из двух операторов S1;S2 (составной оператор).
A4. Если известно:
{ Q } S1 { P1 } и { P1 } S2 { R }, то { Q } S { R }.
Очевидно, что это правило можно сформулировать для последовательности, состоящей из n операторов.
Сформулируем правило для условного оператора (краткая форма).
A5. Если известно:
{ Q and B } S1 { R } и
{ Q not B } => { R },то
{ Q } if B then S1 { R }.
Правило A5 соответствует интерпретации условного оператора в языке программирования.
Сформулируем правило для альтернативного оператора
(полная форма условного оператора ).
A6. Если известно:
{ Q and B } S1 { R } и
{ Q not B } S2 { R },то
{ Q } if B then S1 else S2 { R }.
Сформулируем правила для операторов цикла. Предусловия и постусловия цикла until (до) удовлетворяют правилу:
A7. Если известно:
{ Q and not B } S1 { Q } , то
{ Q } repeat S1 until B { Q and not B }
Правило вводит важное понятие инварианта цикла.
Предикат Q, истинный перед выполнением и после выполнения каждого шага цикла, называется инвариантным отношением или просто инвариантом цикла. В математике термин "инвариантный" означает не изменяющийся под воздействием совокупности рассматриваемых математических операций. В данном случае единственная операция - это выполнение шага цикла при условии истинности Q вначале.
Предусловия и постусловия цикла while (пока) удовлетворяют правилу:
A8. Если известно: { Q and B } S1 { Q } ,
то { Q } while B do S1 { Q and not B }
Правила A1 - A8 можно использовать для проверки согласованности передачи данных от оператора к оператору, для анализа структурных свойств текстов программ, для установления условий окончания цикла. Кроме того, правила можно использовать для анализа результатов выполнения программы, что связано с семантикой программы.

0


Вы здесь » Форум МГОУ Прокопьевск » Московский Государственный Открытый Университет » теория вычислительных процессов