- •10. Написание формальных спецификаций
- •Глава II
- •12. Предварительные замечания о процессе разработки программ
- •12.1. Жизненный цикл математического обеспечения
- •12. Предварительные замечания о процессе разработки программ
- •12.1. Жизненный цикл математического обеспечения
- •12.2. Анализ требований
- •12.3. Пример задачи
- •13.1. Обзор процесса проектирования
- •13.2.1. Вводный раздел
- •13.2.2. Разделы абстракций
- •13.8. Абстракция строки
- •13.9. Обзор и обсуждение
- •14. Этап перехода от проектирования к реализации
- •14.1. Оценка проекта
- •14.1.1. Корректность и эффективность
- •14.1.2. Структура
- •15.2. Выбор подхода
Глава II
множество переменных программы на некоторый хорошо упоря-^ Л^енный набор. (Набор называется полностью упорядоченным,! ^^И каждый его член можно сравнить с любым другим его членом. •"• ^^ностью упорядоченный набор называется хорошо упорядоченным, если любое его непустое подмножество имеет последний ^^Мент.) Неотрицательные и положительные целые числа яв-•1я(о1.^д хорошо упорядоченными. Множество всех целых чисел ^^овым не является, поскольку в нем отсутствует последний -^•^Мент. Набор неотрицательных рациональных чисел также не является хорошо упорядоченным, поскольку в нем имеются ""Аг^ножества, не содержащие последнего элемента, например все положительные рациональные числа. В данной главе мы будем "Пользовать неотрицательные числа в качестве области измене-"^ наших функций декремента.
Функция декремента должна удовлетворять двум условиям:1.Мы должны показать, что каждая итерация цикла умень-шз^-fзначение, в которое отображается функция декремента.
2.Мы должны также показать, что конъюнкция инварианта ^^ла и логического выражения, принимающего значениеtrue^^Ом случае, если функция декремента отображается в последний ^^Мент хорошо упорядоченного набора, имплицирует отрицание "Р^Диката управления циклом. Другими словами, что цикл будет з^ершен.
^^и мы можем представить функцию декремента с данными свойствами, то и можем из этого заключить, что цикл завершается. ^^ следует из того факта, что каждый раз при прохожденииi^^-лафункция декремента должна отображаться в меньшее ^^Чение (условие 1);имеется только конечное число значений ^^Нду начальным значением функции декремента и последним элементом в области ее изменения (свойство хорошо упорядо-^^»шх наборов); при достижении программой состояния, в ко-^Ром функции декремента отображаются в последний элемент, "Р^исходит выход из цикла (условие 2).Полученный результат ^"^.погичен правилу инварианта цикла:даже если через программу имеется бесконечное число маршрутов, мы можем говорить о воз-мо^сности завершения программы, анализируя только конечное чис^о маршрутов.
В нашем примере подходящей функцией декремента может ^У>кить (х —у). Для доказательства того, что цикл
х^о&у=0 {% инвариант х^у
% декремент выражения (х—у) в диапазоне неотрицательных целых % чисел
^•hile ~ (у = х) do у :== у+ I end} У — х
^^1"да завершается, мы должны показать, что
обзор процесса верификации программ *ч«»
1.функция декремента уменьшается в теле цикла ^у) =do &~ (у-х) &х >у {у :=у +Ц (х— у) <do
1.функция декремента не увеличивается в процессе проверки 1)вия выполнения цикла
•»-у) ==do1~(х ==у); (х— у) <do
S. При достижении функцией декремента значения 0цикл вршается
„-у) = 0 &(х >у) ^ ~ (~(у =х))f' ^ эти выражения легко упрощаются до значенияTRUE.
1Мы начали с наблюдения, что основная проблема, связанная задачей, содержащей цикл, заключается в невозможности про-меровать все проходы через программу. Из этого следуют два вода.
1.Доказательство утверждения о том, что цикл завершается, является более тривиальным^
2.Сразу не очевидно, как ограничить число шагов, касаются описания поведения программы, до конечного числа. у1Ы рассмотрели две эти проблемы отдельно друг от друга. Сна-1&ла мы доказали, что если цикл завершится, то он завершится Состоянием, удовлетворяющим постусловию. Это называется аргументомчастичной корректности,и в данном случае критическим указывается отыскание подходящего инварианта цикла. Затем 1{ы анализировали условия завершения цикла. В этом случае 1важно было отыскать соответствующую функцию декремента. [•Комбинация частичной корректности и условия завершения на-1аываетсяполной корректностью.
1^ Студенты часто испытывают затруднения при изучении инвариантов циклов. К счастью, нахождение подходящего инварианта ,не представляет труда. Если вариант неподходящий, то по крайней мере один шаг в проверке не даст положительного результата. В большинстве случаев исследование ситуации о ошибочным инвариантом позволяет быстро решить проблему.
11.3. Разбор процедур
Введение процедур усложняет систему доказательств, необходимую для анализа программ, однако это также уменьшает сложность доказательств в этой системе, что аналогично введению процедур, которые усложняют определение языка программирования, однако упрощает написание программ на этом языке. Грамотное использование процедур облегчает понимание программы и применение по отношению к ней формальных доказательств. Мы будем отделять корректность реализации процедуры от тех
252 Глава II
ий обзор процесса верификации программ
зации. В противном случае мы не смогли бы показать, что удал^ ние одной копии элемента будет достаточным для удаления егс»1 из набора.
11.5. Несколько замечаний по поводу формального анализа
Из вышесказанного очевидно, что формальные рассуждения ' о программах предполагают большой объем утомительных манипуляций с символами. Этот процесс не относится к легким или приятным. Очевидно, что это —идеальная работа для вычислительной машины. Человек должен предоставить 1)спецификацию проверяемой программы; 2)предполагаемую реализацию этой программы; 3)спецификации процедур и типов, используемых в реализации; 4)инвариант цикла и функцию декремента для каждого из циклов и 5)инвариант представителя и функцию абстракции для каждого кластера.
Не следует полагать, что эта информация получается как следствие проверочной стадии. Весь рассмотренный подход к программированию базируется на построении спецификаций в процессе создания программы. Инварианты, функции декремента и функции абстракций должны быть включены как часть программной документации.
Располагая рассмотренной информацией, вторая стадия верификации программы — создание проверочных условий — подчиняется алгоритмизации и, следовательно, может быть получена программно. Большинство манипуляций с формулами предполагает генерацию проверочных условий.
Окончательная стадия проверки — упрощение проверочных условий — может быть частично автоматизирована. Большая часть проделанных в данной главе упрощений производилась при помощи программ, доказывающих теоремы. Однако в общем случае все может складываться не столь удачно. Программа проверки может оказаться не в состоянии доказать, что некоторая программа соответствует своей спецификации, при следующих условиях:
1.Дедуктивные возможности проверочной программы не позволяют ей получить из теоремы необходимый предикат. Имеется ряд любопытных областей, в которых невозможно написать доказывающие теоремы программы, отвечающие на все необходимые вопросы.
2.Проверяемая программа соответствует своей спецификации, однако спецификации внутренних процедур являются слишком слабыми, чтобы доказать это.
3. Проверяемая программа соответствует своей спецификации, однако инварианты цикла, функции декремента, инварианты пред-
двления или функции абстракции являются неудовлетвори-дьными.
4.Корректность программы зависит от некоторого факта, стоящегося к области ее применения. В идеальном случае информа-^я такого рода должна быть включена в спецификацию или дру-ую документацию.
' 5.Программа не удовлетворяет своей спецификации. В этом дучае «неправильной» может быть спецификация, программа или;обе одновременно.
Первая из описанных причин неудач представляет собой серьез-ю проблему в задаче верифицирования программ. Ее обычно 1жно обойти, снабдив проверочную программу соответству-„.цими леммами. К сожалению, ввод необходимых лемм часто Оказывается затруднительным и не соответствует принципам доказательств, используемым проверяющей программой. Во-вторых, третий и четвертый случаи часто являются следствием ^ошибок в программе или документации. Обнаружение таких оши-'бок существенно облегчает проверку программы. Не существует 'способа, позволяющегоaprioriопределить, является ли программа -или спецификация «неправильной». Выявление таких проблем является первоочередной задачей процесса верификации программы. ^
^1.6. Заключение
В этой главе мы рассматривали программы как некоторый текст и анализировали его с этой же точки зрения. Мы связывали с языком программирования набор правил доказательства, который позволял сократить формулу общей корректности вида
Предусловие ^текст программы} Постусловие
до предиката, значение которого не зависело от языка программирования. Для того чтобы это стало возможным, мы ввели в текст программы различную вспомогательную информацию.
Для работы с циклами был введен инвариант цикла и функция декремента. Функции декремента используются для доказательства возможности завершения цикла. Инварианты цикла сокращают анализ произвольно большого числа проходов через содержащую цикл программу до относительно небольшого числа.
При работе с процедурами основной акцент делался на спецификациях. Хотя правила доказательства для процедур довольно сложны (были рассмотрены упрощенные правила), они играют важную роль в организации структурного анализа программ. Главной особенностью является тот факт, что анализ реализации процедуры не зависит от анализаобращений^ней.'Спецификации также играют существенную роль при анализе типов. А типы, аналогично процедурам, помогают структуриро-
254 Глава II
Краткий обзор процесса верификации программ
вать анализ. Доказательство того, что реализация типа удовлетворяет его спецификации, полностью независимо от доказательств для тех частей программы, которые используют данный тип. Функция абстракции и инвариант представления играют существенные роли в анализе корректности реализации типа. Функция абстракции является связующим звеном между абстрактными объектами, на которые осуществляется ссылка в спецификациях типа, и конкретными объектами в реализации. Инвариант представления отражает взаимосвязь между реализациями операций типа. Это позволяет нам анализировать реализацию каждой операции отдельно от других.
Польза от инвариантов цикла, функций декремента, инвариантов представления и функций абстракции не ограничивается формальной верификацией. Они полезны и для самого процесса реализации, поскольку содержат утверждения, от которых зависит эта реализация. Их удобно использовать в процессе тестирования как источник различных проверочных случаев, а также в процессе отладки как свидетельство об ошибках. Они также снабжают программу комментариями, полезными для тех, кто впоследствии пытается разобраться в программе.
Понимание программы предполагает неформальный анализ ее смысла. Неформальный анализ во многом напоминает формальный. Он проходит те же стадии и использует ту же информацию. Не удивительно, что разработанные для формального анализа концепции полезны для понимания программ. И возможно, что самым важным результатом процесса формальной верификации явилось идентифицирование и определение этих концепций.
элемент в этом массиве. Напишите спецификацию процедуры max. array, которая принимает в качестве аргументов два непустых целочисленных массива и возвращает тот массив, который содержит максимальный элемент. (Примечание; вам не требуется реализовывать процедуру max-elem.)
11.4. Напишите спецификацию процедуры, которая изменяет порядок следования элементов в массиве целых чисел на обратный. Напишите процедуру, удовлетворяющую этой спецификации, а затем докажите полную корректность • вашей процедуры.
11.5. Напишите спецификацию для типа стек, не являющегося ограниченным. Он должен иметь операции со следующими заголовками:
new = proc ( ) returns (s: stack) push = proc (s: stack, i: int) pop == proc (s: stack) returns (i: int)
Приведите реализацию для этого типа, включая инвариант представления и функцию абстракции. Докажите, что ваша реализация удовлетворяет спецификации.
11.6. Проанализируйте взаимосвязь между верификацией программы и про-цессом отбора данных для тестирования, приведенным в разд. 9,1.
Дополнительная литература
Cries, David) 1981. The Science of Programming. New York: Springer-Verlag. Hoare, C. R., An axiomatic basis for computer programming. Communications of the ACM 12 (10): 576—583. Reprinted in Programming Methodology, A Collection of Articles by Members of IFIP WG2.3, edited by David Gries (New York: Springer-Verlag, 1978).
Jones, Cliff B., 1980. Software Development, A Rigorous Approach. Engle-wood Cliffs, N. J.: Prentice-Hall International.
London, Ralph L., Mary Shaw, and William A. Wulf, 1981. Abstraction and verification in Alphard: a symbol table example. In Alphard: Form and Content, edited by Mary Shaw (New York: Springer-Verlag), pp. 161—190.
Упражнения
11.1. Приведите инвариант цикла для процедуры sum.positive, приведенной на^ис. 11.2. Докажите корректность данной реализации.
11.2. Завершите проверку реализации целочисленного набора intset доказательством значимости каждой из формул, приведенных на рис. 11.7.
11.3. Напишите спецификацию процедуры max-elem, которая принимает в качестве аргумента непустой массив целых чисел и возвращает наибольший
Предварительные
замечания о процессе разработки программ
