2. Языки программирования (в том числе и
языки представления алгоритмов) относятся к
знаковым системам. В общей теории знаковых
систем (семиотике) принято выделять
синтактику, трактующую правила построения
текстов исследуемого языка, семантику,
изучающую связи между текстами и смыслом,
который они выражают, и прагматику,
исследующую отношения между знаковыми
системами и теми, кто ими пользуется.
К концу 1960-х годов сложились три основных
подхода к формальному описанию семантики
языков программирования.
3. 1.
Операционный подход: семантика
описывается в терминах некоторой машины,
чаще всего – абстрактной. Фактически этот
подход реализуется всякий раз, когда создается
«базовый» интерпретатор языка, служащий
эталоном правильности любой другой
реализации. Пионером здесь был Дж. Мак-Карти
с его Лисп-интерпретатором. В процессе работы
машины меняется состояние программы –
совокупность значений ее переменных.
Результат исполнения программы – это
состояние, при котором машина завершает
работу по данной программе. Естественно, оно
зависит от начального состояния – того, в
котором машина начала работать.
4. 2.
Аксиоматический или деривационный
подход, основанный на системе аксиом,
постулирующих свойства основных
конструкций языка, и правил вывода,
позволяющих получать исходя из этих
аксиом свойства любых программ и их
фрагментов (П.Наур, Р.Флойд, Ч.А.Р.Хоор
и др.). Таким образом, строится некая
формальная теория – «исчисление
программ», формулы которой выражают
утверждения о состоянии программы.
5. 3.
Денотационный подход, получивший также
название математической семантики. Здесь, как
и в операционном подходе, в основу кладется
модель (интерпретация) алгоритма, но она
описывается не в терминах машины, а более
традиционными для математики средствами – в
терминах некоторой совокупности множеств,
отношений и отображений. Эти средства должны
описывать результаты исполнения машинной
программы в целом и ее отдельных частей,
точнее – связь между начальным и
заключительным состоянием. Основы этого
подхода заложили Д.Скотт, К.Стрейчи, Я.В. де
Баккер и др.
6. Операционный подход ближе других стоит
к задаче создания как интерпретаторов
языков программирования, так и
компиляторов для них. По традиции его
считают первичным, отражающим
содержательную сторону
программирования. Однако существует и
иная позиция по отношению к
операционному взгляду на
программирование, отдающая приоритет
функциональному, денотационному
подходу (Дж.Бакус и др.).
7. Предельная строгость и четкость – качества,
которыми программисты (как и плоды их
трудов – программы, спецификации и пр.)
должны обладать в значительно большей
степени, чем даже математики, так как
последние вправе рассчитывать на интеллект
и интуицию своих коллег, тогда как первые
имеют дело с компьютерами – безмозглыми
машинами. Машинно-ориентированные
понятия и термины (ячейка, адрес и пр.) в чемто помогают установить требуемую четкость
при описании семантики, но основная цель –
добиться ясности понимания самих
программных конструкций, языковых средств,
используемых при их написании. Отсюда и
берет начало стремление отойти от
операционного подхода в направлении к двум
другим.
8. Аксиоматический подход в большей
степени помогает составлять правильные
программы, не полагаясь на их отладку с
помощью тестирования. Однако, как и для
традиционных логических исчислений,
надо уметь обосновывать
общезначимость аксиом и
состоятельность правил вывода
исчисления программ. Такую возможность
предоставляет денотационный подход, в
котором на основе правил работы
абстрактной машины в операционной
семантике используется хорошо развитый
и изученный аппарат теории множеств и
формальной логики.
9. 3. Исчисление программ
Аксиоматическая (деривационная) семантика
имеет своим предметом логическое
исчисление, использующее на ряду с обычными
логическими формулами исчисления
высказываний или исчисления предикатов
(обычно первого порядка) формулы
специального вида, содержащие внутри себя
операторы некоторого алгоритмического языка
и описывающие их логические свойства – связь
между состояниями переменных программы до
и после исполнения этих операторов. Основы
аксиоматической семантики программ были
заложены в работах Р.Флойда и Ч.А.Хоора.
10. Будем использовать операторные формулы двух
видов: слабые формулы p{S}q и сильные
формулы p[S]q, где S – оператор, p и q –
логические выражения, называемые,
соответственно, предусловием и постусловием
для оператора S.
Слабая формула имеет следующий
содержательный смысл: если исполнение
оператора S началось из состояния w,
удовлетворяющего условию p, и завершилось
(что не гарантируется) в некотором состоянии w1,
обозначаемом также s(w), то последнее
удовлетворяет условию q:
(p{S}q) ≡ ∀w(p(w) ⊃ ∀w1(w1=s(w) ⊃ q(w1))).
11. •
•
•
•
•
•
•
•
Смысл сильной формулы: если оператор S начал
исполняться из состояния w удовлетворяющего
условию p, то его исполнение обязательно
завершается и любое выработанное в результате
состояние w1 удовлетворяет условию q:
(p[S]q) ≡ ∀w(p(w) ⊃ ∃w1(w1=s(w)) ∧ ∀w2(w2=s(w) ⊃ q(w2))).
Пусть имеются следующие операторы:
skip – пустой оператор,
abort – неисполнимый оператор,
v := e – оператор присваивания,
Write(e1,…,em) – оператор вывода,
Read(v1,…,vn) – оператор ввода,
S1; …; Sk – последовательность операторов,
if b then S1 else S2 fi – условный оператор,
while b do S od – оператор цикла,
12. где
• v, v1,…,vn – переменные,
• e, e1,…,em – выражения,
• S, S1, …, Sk – операторы,
• b – логическое выражение.
Тогда свойства операторов описываются
следующими аксиомами:
• (A1) p[skip]p,
• (A2) p{abort}p,
• (A3) p(v){v:=e(v)}∃i (p(i) ∧ v=e(i)),
• (A4) p(e)[v:=e]p(v),
• (A5) p[Write (e1,…,em)]p,
• (A6) true{Read (v1,…,vn)}v1 = v01 ∧…∧ vn = v0n,
13. где
• i – обозначение, отличное от всех обозначений
переменных в программе,
• v, v1,…,vn – переменные,
• e, e1,…,em – выражения,
• v01,…, v0n – константы, фиксирующие вводимые
оператором Read значения переменных.
• Правила вывода:
• (R1) p ⊃ p1, p1{S}q1, q1 ⊃ q ⊢ p{S}q,
• (R2) p{S}p1, p1 {S1}q ⊢ p{S; S1}q,
• (R3) p ∧ b{S1}q, p ∧ ¬b{S2}q ⊢ p{if b then S1 else S2
fi}q,
• (R4) p ∧ b{S}p ⊢ p{while b do S od}p ∧ ¬b.
В последнем правиле формула (условие) p
называется инвариантом цикла.
14. Единственная посылка правила требует,
чтобы это условие, если оно было истинно
перед исполнением тела S оператора
цикла, оставалась истинным и после его
завершения.
Тривиальный (слабейший) инвариант true
существует всегда, а инвариант, во всем
существенном отражающий свойства
оператора цикла при данных b и p, найти
очень нелегко, если вообще возможно.