- •07.11.12 Лекция 8
- •7. Технология предикатного программирования 7.4. Гиперфункции
- •Структурное
- •Неудобные ситуации
- •Пример. Решение системы линейных уравнений
- •Определение гиперфункции :
- •elemTwo(list (int) s : int e : )
- •Пример 7.4. Определим гиперфункцию Comp следующей спецификацией:
- •Если оператор продолжения ветви содержит только оператор перехода, то оператор перехода переносится в
- •Свойства гиперфункций
- •Пример. Сумма чисел в строке
- •Обобщение исходной задачи :
- •Отметим, что можно было бы использовать гиперфункцию вида: перваяЦифра1(string s: : string r),
- •Сумма(string s, nat m : nat n)
- •Чтобы получить хвостовую рекурсию для предиката числоВстроке необходимо ввести накопитель для вычисления значения
- •взятьЧисло(string r, nat p: nat v, string t)
- •Сумма:
- •числоВстроке(char e, string s: nat v, string s) { взятьЧисло(s, val(e): v, s)
- •перваяЦифра(string s:
- •Подставим определения предикатов взятьЧисло и перваяЦифра на место вызовов. Проведем очевидные упрощения.
- •Поскольку внутренний цикл в теле предиката Сумма не имеет нормального выхода, можно заменить
- •Кодирование строк
- •Имеется недостаток программы (7.39): если строка s завершается цифрой, то проверка исчерпания строки
- •Система линейных уравнений
- •diagEl(nat n, MATR(n) a, VEC(n) b, nat k:
- •perm(nat n, MATR(n) a, VEC(n) b, nat k, m:
- •norm(nat n, k, MATR(n) a, VEC(n) b: MATR(n) a’, VEC(n) b’) pre 1
- •subtrCol(nat n, MATR(n) a, VEC(n) b, nat k:
- •На втором этапе реализуется замена хвостовой рекурсии циклами. Результатом проведения двух этапов трансформации
- •Jord(nat n, MATR(n) a, VEC(n) b, nat k: MATR(n) a, VEC(n) b :
- •perm(nat n, MATR(n) a, VEC(n) b, nat k, m: MATR(n) a, VEC(n) b
- •subtrLines(nat n, k, MATR(n) a, VEC(n) b: MATR(n) a, VEC(n) b)
- •На третьем этапе проведем подстановку следующую серию подстановок тел определений на место вызовов:
- •Lin(nat n, MATR(n) a, VEC(n) b: VEC(n) b : )
- •Lin(nat n, MATR(n) a, VEC(n) b: VEC(n) b : )
- •Нетривиальным является устранение неиспользуемых вычислений : Lin(nat n, MATR(n) a, VEC(n) b: VEC(n)
07.11.12 Лекция 8
Предикатное программирование
7. Технология предикатного программирования
7.4. Гиперфункции
Неудобные ситуации в программировании Гиперфункция и оператор продолжения ветвей Свойства гиперфункций Пример. Вычисление суммы чисел в строке
Пример. Решение системы линейных уравнений
7. Технология предикатного программирования 7.4. Гиперфункции
Структурное
программирование
Эдвард Дейкстра
begin <оператор1> ; <оператор2> end
if <условие> then <оператор1> else <оператор2> end while <условие> do <оператор> end
Неудобные ситуации
elemTwo(list(int) s: int e, bool exists ) { if (s = nil s.cdr = nil) exsts = false
else { e = s.cdr.car || exists = true }
} post exists = (s nil & s.cdr nil) & (exists e = s.cdr.car)
elemTwo(s: e, exists); if (exists) B(e…) else D(…) |
(7.13) |
if ( s = nil s.cdr = |
nil ) |
(7.14) |
{ exist = false; if (exist) B(e…) else D(…) } |
|
|
else {{e = s.cdr.car |
|| exist = true }; if (exist) B(e…) else D(…)} |
if (s = nil s.cdr = nil) D(…) else { e = s.cdr.car; B(e…) }
Пример. Решение системы линейных уравнений
n
aijxj bj, j 1..n LinEq(a, x, b)
i 1
type VEC(nat k) = array (real, 1..k);
type MATRICE(nat k) = array(real, 1..k, 1..k); Lin(nat n, MATRICE(n) a, VEC(n) b: VEC(n) x)
pre det(n, a) 0 |
// > |
post LinEq(n, a, x, b); |
Иногда требуется решить более общую задачу: |
Lin1(nat n, MATRICE(n) a, VEC(n) b: bool c; VEC(n) x) post c = (det(n, a) 0) & (c LinEq(n, a, x, b));
Lin1(n, a, b: c; x); if (c) B(x…) else D(…)
P(x) { A(x: y, z, c) } c = C(x) Q(x, y, z); x, y и z возможно пустые
Q(x, y, z) (C(x) S(x, y)) ( C(x) R(x, z)) |
(7.16) |
|
Предикаты S(x, y) и R(x, z) функции S: x y и R: x z. |
|
|
C(x) истинно |
Q(x, y, z) совпадает с S: x y |
|
C(x) ложно |
Q(x, y, z) совпадает с R: x z. |
|
Q(x, y, z) результат склеивания функций S: x y и R: x z. В Q(x, y, z) не определено значение набора z, если C(x) истинно. Аналогично, не определено значение y при ложном C(x).
Лемма 7.10. Если предикат S(x, y) тотален в области P(x) & C(x), а R(x, z) в области P(x) & C(x), то
спецификация предиката A(x: y, z, c) является тотальной.
Лемма 7.11. Если предикат S(x, y) является однозначным в области P(x) & C(x), а R(x, z) в области P(x) & C(x), то спецификация предиката A(x: y, z, c) является однозначной.
P(x) { A(x: y, z, c) } c = C(x) Q(x, y, z) |
|
Q(x, y, z) (C(x) S(x, y)) ( C(x) R(x, z)) |
(7.16) |
Пусть вызов A используется во фрагменте вида: |
|
A(x: y, z, c); if (c) B(y: u) else D(z: u) |
(7.17) |
Построим оператор A’(x: y, z) модификацией A(x: y, z). Всякий оператор c = true заменим на #1, а c = false на #2.
#1 и #2 обозначают операторы goto 1 и goto 2.
A’(x: y, z); if (c) 1: B(y: u) else 2: D(z: u) |
(7.18) |
A’(x: y, z); 1: B(y: u); goto 3; 2: D(z: u); 3: |
(7.19) |
Предикат A’(x: y, z) будем записывать в виде A(x: y #1: z #2) и называть гиперфункцией с двумя ветвями. Метки 1 и 2 являются метками ветвей.
A(x: y #1: z #2) case 1: B(y: u) case 2: D(z: u) |
(7.20) |
Допускается более компактная запись: |
|
A(x: y : z) case B(y: u) case D(z: u) |
(7.21) |
Определение гиперфункции : |
|
A(x: y #1: z #2) pre P(x); pre 1: C(x) |
(7.22) |
{ A’(x: y, z) } post 1: S(x, y); post 2: R(x, z);
Блок A’(x: y, z) тело гиперфункции, P(x) общее предусловие, C(x) предусловие первой ветви
гиперфункции, S(x, y) постусловие первой ветви, R(x, z)
постусловие второй ветви.
Метки ветвей в определении предиката можно опустить:
A(x: y: z ) pre P(x); pre 1: C(x) { … } … |
(7.23) |
A(x: y #1: z #2) case 1: B(y: u) case 2: D(z: u) |
(7.20) |
case 1: B(y: u) и case 2: D(z: u) операторы продолжения ветви, а вместе обработчик ветвей. Обработчику
ветвей предшествует оператор, содержащий операторы перехода. Если предшествующий оператор нормально завершается (не оператором перехода), то обработчик ветвей пропускается.
elemTwo(list (int) s : int e : )
pre 1: s nil & s.cdr nil |
|
|
{ if (s = nil |
s.cdr = nil) |
#2 |
else |
{ e = s.cdr.car |
#1 } |
} post 1: e = s.cdr.car;
elemTwo(list (int) s : int e #1 : #2)
elemTwo(s: e: ) case B(e…) case D(…)
elemTwo B(e…)
D(…)
Пример 7.4. Определим гиперфункцию Comp следующей спецификацией:
Comp(list (int) s: : int d, list (int) r) pre 1: s = nil
post 2: d = s.car & r = s.cdr;
Построим определение предиката elemTwo через гиперфункцию Comp без полей car, cdr и конструктора nil.
elemTwo(list (int) s: int e #1: #2) pre 1: s != nil & s.cdr != nil
{int e1; list (int) s1, s2; Comp(s: : e1, s1) case #2
case {
Comp(s1: : e, s2) case #2
case #1
}
} post 1: e = s.cdr.car;