Логика Хоара (также известная как логика Флойда-Хора или правила Хоара ) — это формальная система с набором логических правил для строгого рассуждения о правильности компьютерных программ . Она была предложена в 1969 году британским ученым-компьютерщиком и логиком Тони Хоаром и впоследствии усовершенствована Хоаром и другими исследователями. [1] Оригинальные идеи были заложены в работе Роберта Флойда , опубликовавшего аналогичную систему [2] для блок-схем .
Центральной особенностью логики Хоара является тройка Хоара . Тройка описывает, как выполнение фрагмента кода меняет состояние вычислений. Тройка Хоара имеет вид
где и — утверждения , а — команда . [примечание 1] называется предусловием и постусловием : когда предусловие выполнено , выполнение команды устанавливает постусловие. Утверждения — это формулы в логике предикатов .
Логика Хоара предоставляет аксиомы и правила вывода для всех конструкций простого императивного языка программирования . В дополнение к правилам для простого языка, описанным в оригинальной статье Хоара, с тех пор Хоар и многие другие исследователи разработали правила для других языковых конструкций. Существуют правила для параллелизма , процедур , переходов и указателей .
Используя стандартную логику Хоара, можно доказать только частичную правильность . Полная корректность дополнительно требует завершения , что можно доказать отдельно или с помощью расширенной версии правила While. [3] Таким образом, интуитивное прочтение тройки Хоара таково: Всякий раз, когда сохраняется состояние до выполнения , то будет сохраняться и после, или не завершается. В последнем случае «после» не существует, поэтому может быть вообще любое утверждение. Действительно, можно выбрать ложь, чтобы выразить то, что не прекращается.
«Завершение» здесь и далее в этой статье подразумевается в более широком смысле, что вычисление в конечном итоге будет завершено, то есть подразумевает отсутствие бесконечных циклов; это не означает отсутствия нарушений предела реализации (например, деления на ноль), что приводит к преждевременной остановке программы. В своей статье 1969 года Хоар использовал более узкое понятие завершения, которое также влекло за собой отсутствие нарушений предела реализации, и выразил свое предпочтение более широкому понятию завершения, поскольку оно сохраняет утверждения независимыми от реализации: [4]
Еще один недостаток аксиом и правил, приведенных выше, заключается в том, что они не дают оснований для доказательства успешного завершения программы. Невозможность завершения может быть связана с бесконечным циклом; или это может быть связано с нарушением ограничения, определенного реализацией, например, диапазона числовых операндов, размера хранилища или ограничения времени операционной системы. Таким образом, обозначение « » следует интерпретировать «при условии, что программа успешно завершается, свойства ее результатов описываются ». Довольно легко адаптировать аксиомы так, чтобы их нельзя было использовать для предсказания «результатов» незавершающихся программ; но фактическое использование аксиом теперь будет зависеть от знания многих функций, зависящих от реализации, например, размера и скорости компьютера, диапазона чисел и выбора метода переполнения. Помимо доказательств отсутствия бесконечных циклов, вероятно, лучше доказать «условную» корректность программы и полагаться на то, что реализация выдаст предупреждение, если ей пришлось отказаться от выполнения программы в результате нарушения какого-либо условия. предел реализации.
Правило пустого оператора утверждает, что оператор пропуска не меняет состояние программы, поэтому все, что истинно до пропуска, остается истинным и после него. [заметка 2]
Аксиома присваивания гласит, что после присваивания любой предикат, который ранее был истинным для правой части присваивания, теперь справедлив для переменной. Формально, пусть P — утверждение, в котором переменная x свободна . Затем:
где обозначает утверждение P , в котором каждое свободное вхождение x заменено выражением E .
Схема аксиом присваивания означает, что истинность эквивалентна истинности после присваивания P . Таким образом , согласно аксиоме присваивания, если бы оно было истинным до присвоения, то P было бы истинным после этого. И наоборот, если до оператора присваивания было ложь (т. е. истина), то после этого P должно быть ложным.
Примеры допустимых троек включают в себя:
Все предусловия, которые не изменяются выражением, могут быть перенесены в постусловие. В первом примере присвоение не меняет того факта, что , поэтому в постусловии могут присутствовать оба оператора. Формально этот результат получается путем применения схемы аксиом, где P равно ( и ), что дает существование ( и ), которое, в свою очередь, можно упростить до заданного предварительного условия .
Схема аксиом присваивания эквивалентна утверждению: чтобы найти предусловие, сначала возьмите постусловие и замените все вхождения левой части присваивания правой частью присваивания. Будьте осторожны и не пытайтесь сделать это наоборот, следуя этому неправильному образу мышления: ; это правило приводит к бессмысленным примерам, таким как:
Еще одно неверное правило, кажущееся заманчивым на первый взгляд : ; это приводит к бессмысленным примерам вроде:
Хотя данное постусловие P однозначно определяет предусловие , обратное неверно. Например:
являются действительными экземплярами схемы аксиом присваивания.
Аксиома присвоения, предложенная Хоаром, не применяется , когда к одному и тому же хранимому значению может относиться более одного имени. Например,
неверно, если x и y относятся к одной и той же переменной ( псевдоним ), хотя это правильный пример схемы аксиом присваивания (с обоими и is ).
Правило композиции Хоара применяется к последовательно выполняемым программам S и T , где S выполняется до T и записывается ( Q называется промежуточным условием ): [5]
Например, рассмотрим следующие два примера аксиомы присваивания:
и
По правилу последовательности можно сделать вывод:
Другой пример показан в правом поле.
Условное правило гласит, что постусловие Q , общее для частей then и else , также является постусловием всего оператора if...endif . [6] В частях then и else неотрицаемое и инвертированное условие B могут быть добавлены к предварительному условию P соответственно. Состояние B не должно иметь побочных эффектов. Пример приведен в следующем разделе.
Это правило не содержалось в оригинальной публикации Хоара. [1] Однако, поскольку заявление
имеет тот же эффект, что и конструкция одноразового цикла
условное правило может быть получено из других правил Хоара. Аналогичным образом правила для других производных программных конструкций, таких как for цикл, do...until цикл, переключатель , перерыв , продолжить , могут быть сведены путем преобразования программы к правилам из оригинальной статьи Хоара.
Это правило позволяет усилить предусловие и/или ослабить постусловие . Он используется, например, для достижения буквально идентичных постусловий для частей then и else .
Например, доказательство
необходимо применить условное правило, которое, в свою очередь, требует доказать
для тогдашней части, и
для остальной части.
Однако правило назначения для части then требует выбрать P как ; применение правил, следовательно, дает
Правило последствий необходимо для усиления предусловия , полученного из правила присваивания, до требуемого для условного правила.
Аналогично, для остальной части правило присваивания дает результат
следовательно , правило следствий должно применяться с и и , соответственно, чтобы снова усилить предварительное условие. Неформально, эффект правила последствий заключается в том, чтобы «забыть» то, что известно при вводе части else , поскольку правило присваивания, используемое для части else , не нуждается в этой информации.
Здесь P — инвариант цикла , который должен сохраняться телом цикла S. После завершения цикла этот инвариант P все еще сохраняется и, более того, должен был привести к завершению цикла. Как и в условном правиле, B не должен иметь побочных эффектов.
Например, доказательство
к тому времени правило требует доказать
которое легко получить по правилу присваивания. Наконец, постусловие можно упростить до .
Другой пример: правило while можно использовать для формальной проверки следующей странной программы для вычисления точного квадратного корня x из произвольного числа a , даже если x — целочисленная переменная, а a — не квадратное число:
После применения правила while, когда P истинно , осталось доказать
что следует из правила пропуска и правила последствий.
На самом деле странная программа отчасти правильна: если бы она завершилась, то наверняка x (случайно) содержал бы значение квадратного корня. Во всех остальных случаях оно не будет прекращено; поэтому это не совсем правильно.
Если приведенное выше обычное правило while заменить следующим, исчисление Хоара также можно использовать для доказательства полной корректности , т. е. завершения, а также частичной корректности. Обычно здесь вместо фигурных скобок используются квадратные скобки, чтобы указать на другое представление о корректности программы.
В этом правиле, помимо сохранения инварианта цикла, также доказывается завершение с помощью выражения t , называемого вариантом цикла , значение которого строго уменьшается по отношению к обоснованному отношению < на некотором множестве доменов D во время каждой итерации. Поскольку < обосновано, строго убывающая цепочка членов D может иметь только конечную длину, поэтому t не может уменьшаться вечно. (Например, обычный порядок < хорошо обоснован для целых положительных чисел , но ни для целых , ни для положительных действительных чисел ; все эти множества подразумеваются в математическом, а не в вычислительном смысле, все они, в частности, бесконечны.)
Учитывая инвариант цикла P , условие B должно подразумевать, что t не является минимальным элементом D , поскольку в противном случае тело S не могло бы уменьшать t дальше, т. е. посылка правила была бы ложной. (Это одно из различных обозначений полной правильности.) [примечание 3]
Возвращаясь к первому примеру предыдущего раздела, для доказательства полной корректности
правило while для полной корректности может быть применено, например, D — это неотрицательные целые числа обычного порядка, а выражение t — , что, в свою очередь, требует доказательства
Неформально говоря, нам нужно доказать, что расстояние уменьшается в каждом цикле цикла, но всегда остается неотрицательным; этот процесс может продолжаться лишь конечное число циклов.
Предыдущую цель доказательства можно упростить до
что можно доказать следующим образом:
Во втором примере предыдущего раздела, конечно, не может быть найдено выражение t , которое уменьшалось бы пустым телом цикла, поэтому завершение не может быть доказано.