
- •2.6. Заключение .............................................................................................................. 22
- •4. Язык исчисления вычислимых предикатов,
- •4.12. Однозначность предикатов .......................................................................................... 45
- •5. Семантика языка предикатного программирования.
- •6.9. Формулы .......................................................................................................................... 78
- •6.9. Процессы ......................................................................................................................... 79
- •6.11. Императивное расширение .......................................................................................... 82
- •7.8. Заключение ................................................................................................................... 108
- •Введение в курс предикатного программирования
- •1. Общее понятие программы
- •1.1. Автоматическая вычислимость
- •1.2. Спецификация программы
- •1.3. Формы спецификации программы
- •Список литературы
- •2. Корректность программ с предикатной спецификацией
- •2.1. Предикатная спецификация программы
- •2.2. Логическая семантика языка программирования
- •2.3. Модель корректности программы
- •2.4. Система правил доказательства корректности операторов
- •2.4.1. Правила для корректного оператора
- •2.4.2. Правила корректности для параллельного оператора
- •2.4.3. Правила корректности для оператора суперпозиции
- •2.4.4. Правила корректности для условного оператора
- •2.5. Система правил вывода программы из спецификации
- •2.5.1. Однозначность предикатов
- •2.5.2. Теорема тождества спецификации и программы
- •2.5.3. Правила корректности для параллельного оператора
- •2.5.4. Правила корректности для оператора суперпозиции
- •2.5.5. Правила корректности для условного оператора
- •2.6. Заключение
- •Список литературы
- •3. Математические основы
- •3.1. Отношения порядка
- •3.2. Наименьшая неподвижная точка
- •3.3. Математическая индукция
- •Список литературы
- •4. Язык исчисления вычислимых предикатов, его логическая и операционная семантика
- •4.1. Структура программы на языке ccp
- •4.2. Система типов данных
- •4.3. Логическая и операционная семантика языка ccp
- •4.4. Семантика вызова предиката
- •4.5. Оператор суперпозиции
- •4.6. Параллельный оператор
- •4.7. Условный оператор
- •4.8. Конструктор предиката
- •4.9. Конструктор массива
- •4.10. Программа
- •4.11. Рекурсивные определения предикатов
- •4.12. Однозначность предикатов
- •Список литературы
- •5. Семантика языка предикатного программирования. Методы доказательства корректности предикатных программ
- •5.1. Язык p1: подстановка определения предиката на место вызова
- •5.2. Язык p2: оператор суперпозиции и параллельный оператор общего вида
- •5.3. Язык p2: другое обобщение оператора суперпозиции
- •5.4. Язык p3: выражения
- •5.5. Методы доказательства корректности рекурсивных программ
- •6. Язык предикатного программирования
- •6.1. Введение
- •6.2. Лексемы
- •6.3. Предикаты
- •6.3.1. Определение предиката
- •6.3.2. Спецификация предиката
- •6.3.3. Вызов предиката
- •6.4. Программа
- •6.5. Операторы
- •6.6. Выражения
- •6.7. Типы
- •6.8. Массивы
- •6.8.1. Описание типа массива
- •6.8.2. Вырезка массива
- •6.8.3. Определение массива
- •6.8.4. Объединение массивов
- •6.9. Формулы
- •6.10. Процессы
- •6.11. Императивное расширение
- •Список литературы
- •7. Технология предикатного программирования
- •7.1. Подстановка определения предиката на место вызова
- •7.2. Замена хвостовой рекурсии циклом
- •7.3. Склеивание переменных
- •7.4. Метод обобщения исходной задачи
- •7.5. Трансформация кодирования структурных объектов
- •7.6. Пример. Сортировка простыми вставками
- •7.7. Гиперфункции
- •7.8. Заключение
- •Список литература
4.10. Программа
Программа на языке CCP состоит из конечного набора определений предикатов. Для каждого определения правой частью является оператор суперпозиции (4.16), параллельный оператор (4.19) или условный оператор (4.20). Первичными конструкциями, используемыми для построения операторов, являются вызовы предикатов. Набор определений предикатов программы является замкнутым: вызываемые предикаты являются либо базисными, либо имеет определение в программе.
Имя определяемого предиката должно быть отлично от имен базисных предикатов, имен других определяемых предикатов, а также от имен типов, используемых в программе. Если в программе используется подмножество типа (4.3), то предикат, определяющий подмножество, должен быть базисным или определяемым.
Исполнение программы заключается в исполнении некоторого вызова предиката A(z: u), где A имя предиката, определяемого в программе; z и u наборы переменных. Исполнение программы реализуется следующей последовательностью операторов:
s = newSect(z, u); (4.32)
input(s[z]);
runCall(s, A(z: u));
output(s[u]) .
Здесь s секция, состоящая из переменных наборов z и u. Оператор input реализует ввод некоторых значений набора z в секции s. Далее в секции s исполняется вызов предиката A(z: u).
Предикаты могут быть аргументами и результатами предикатов. Создание нового предиката реализуется вызовом генератора CONS(x, B: A), где CONS обозначает базисный предикат ConsPred или ConsArray, x возможно пустой набор переменных, являющихся фиксируемыми аргументами предиката с именем B (см. разд. 4.8 и 4.9). Генератор создает предикат и присваивает его переменной A предикатного типа.
Для упрощения языка CCP будем считать, что в качестве B в генераторе нельзя использовать переменную предикатного типа. Если все же B переменная предикатного типа, то можно построить эквивалентную программу следующим образом. Допустим, в некотором определении предиката имеется генератор CONS(y, C: B). Заменим этот генератор вызовом G(y : B, F) и добавим в программу два определения:
G(y : B, F) º CONS(y, C: B) || CONS(y, H: F) ;
H(y, x: A) º CONS(y, x, C: A) .
Если далее B передается аргументом некоторого вызова, то необходимо дополнительным параметром передать F. Наконец, мы можем заменить генератор CONS(x, B: A) эквивалентным вызовом F(x: A).
Другое упрощение в языке CCP: имя определяемого или базисного предиката может встречаться в качестве аргумента только в вызовах ConsPred и ConsArray. Если все же имя предиката встречается в вызове другого предиката, его можно эквивалентно заменить переменной предикатного типа.
Пусть в программе имеется вызов A(z: u), где A переменная предикатного типа. Значением переменной A является предикат, генерируемый некоторым вызовом CONS(x, B: A). В качестве представителя значения удобнее использовать предикат B, который назовем заместителем для данного вхождения переменной A. Введем обозначение subs(A) для множества заместителей переменной A. Определим систему правил построения множества subs(A). Допустим, вызов A(z: u) входит в правую часть определения D(…A: …) º … A(…). В данном обозначении опускается вхождение другого вызова в правой части, опускаются вхождения аргументов и результатов предиката D, кроме аргумента A, причем считается, что A может находиться в любой позиции списка аргументов. Далее, пусть имеется вызов D(…C: …), где позиция переменной C соответствует позиции A. Тогда заместители переменной C являются также заместителями A. Данное правило действует также и для определения вида D(…A: ) º … H(…A:…). В итоге имеем:
Правило S1. Пусть имеется определение D(…A: …) º … A(…) или D(…A: …) º … H(…A:…) и вызов D(…C: …). Тогда заместители переменной C являются заместителями A.
При наличии генератора CONS(y, D: E) возможны неявные вызовы предиката D. Если D subs(F) и имеется вызов F(…C: …), то при его исполнении может быть вызвано определение предиката D. Поэтому в правиле S1 вместе с явными вызовами D(…C: …) следует рассматривать и неявные вызовы F(…C: …), причем позиция C сдвигается на число фиксированных аргументов генератора. Через Calls(D) обозначим множество явных и неявных вызовов предиката D. Если в соответствии с правилом S1 для вхождения A(…) будет обнаружен заместитель B для переменной A, то вызов A(…) включается в множество Calls(B). Сформулируем оставшиеся правила.
Правило S2. Пусть имеется определение D(…) º C(…: A…); H(…A: …) или D(…) º C(…: A…); A(…). Тогда всякий заместитель переменной A для вхождения C(…: A…) является также заместителем A для вхождения H(…A: …) или A(…). Если для вхождения A(…) обнаружен заместитель B, то вызов A(…) включается в Calls(B).
Правило S3. Пусть имеется определение H(…: A…) º … C(…: A…), а также вызов H(…: E…) или неявный вызов F(…: E…). Тогда всякий заместитель E для вхождения H(…: E…) (или F(…: E…)) является также заместителем A для вхождения C(…: A…).
Правило S4. Пусть имеется вызов CONS(x, B: A). Тогда subs(A) = {B}.
Определим алгоритм построения заместителей переменных предикатного типа для произвольной программы. В начальный момент каждому вхождению любой переменной предикатного типа сопоставим пустое множество заместителей; для каждого определяемого предиката B множество Calls(B) определим набором явных вызовов B. На каждом шаге алгоритма по всей программе срабатывают правила S1S4 для определений предикатов и вызовов, удовлетворяющих описанным образцам; вызовы предикатов выбираются из текущих множеств Calls. Алгоритм завершается после конечного числа шагов при стабилизации множеств заместителей по всем вхождениям предикатных переменных.
Лемма 4.10. Имеется вызов A(z: u), где A переменная предикатного типа. Допустим, значение A получено исполнением конструктора CONS(x, B: C), а затем передано в A через параметры вызовов. Тогда B subs(A).
Замечание. Для вхождения A(z: u) не все элементы subs(A) достижимы при исполнении, поскольку, например, вызов A(z: u) может находиться внутри условного оператора.
Лемма 4.11. Имеется вызов A(z: u), где A переменная предикатного типа. Тогда (Bsubs(A). Cons(B)) Þ Cons(A). Доказательство следует из лемм 4.7, 4.8, 4.10. □