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