Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lectures / lecture7.ppt
Скачиваний:
7
Добавлен:
06.06.2015
Размер:
283.65 Кб
Скачать

31.10.12 Лекция 7

Предикатное программирование

7. Технология предикатного программирования

Базовые трансформации Подстановка определения на место вызова Замена хвостовой рекурсии циклом Склеивание переменных Метод обобщения исходной задачи

Трансформация кодирования структурных объектов Пример. Сортировка простыми вставками

7. Технология предикатного программирования

Предикатная программа транслируется на императивное расширение языка P с применением оптимизирующих трансформаций.

Базовыми трансформациями являются:

-замена хвостовой рекурсии циклом;

-подстановка определения предиката на место его вызова;

-склеивание переменных, реализующее замену нескольких переменных одной;

-кодирование рекурсивных структур с помощью массивов и указателей.

Итоговая программа по эффективности не уступает написанной вручную и, как правило, короче.

Технология предикатного программирования позволяет воспроизвести любую реализацию, проводимую в императивном программировании.

Для сложных задач предикатная программа на порядок проще ее императивного аналога.

Для функциональных языков не удалось достичь приемлемой эффективности даже с применением изощренных методов оптимизации.

Подстановка определения предиката на место вызова

A(x: y) { S } определение предиката на императивном расширении языка P, а

A(e: z) вызов предиката в теле предиката B.

x, y, z списки переменных, а e список выражений. Подстановка определения предиката на место вызова A(e: z) есть замена вызова

композицией:

| x | = | e |; { S }; | z | = | y |

(7.1)

x и y становятся локальными переменными

Необходимо предварительное систематическое переименование переменных.

Исполнение (7.1) эквивалентно исполнению вызова A(e: z).

|x| = |e| - групповой оператор присваивания

раскрытие группового оператора

| x | = | e |; { S }; | z | = | y |

(7.1)

Вбольшинстве случаев | z | = | y | можно устранить, заменив z на y в операторе S

x состоит из x1, x2, …, xn; а e - e1, e2, …, en. Пусть ej - переменная.

ej должна быть отлична от xj. Почти всегда замена xj на ej дает эквивалентную программу. Склеивание xj с ej уменьшает число переменных на 1 и позволяет удалить xj = ej в групповом операторе |x| = |e|. Склеивание переменных xj и ej корректно кроме случая, когда xj перевычисляется внутри S, что возможно как результат склеивания xj с другими переменными, а переменная ej используется не только в вызове A(e: z), но и после него.

Замена хвостовой рекурсии циклом

Это специальный случаем подстановки определения предиката на место вызова

Рекурсивный вызов определяет хвостовую рекурсию, если:

имя вызываемого предиката совпадает с именем определяемого предиката, в теле которого находится вызов; результаты вызова совпадают с результатами – формальными параметрами

вызов является последней исполняемой конструкцией в определении предиката, содержащем вызов.

last(S) - множество последних исполняемых конструкций в

операторе S. Тогда: last(A; B) = last(B),

last(if (C) A else B) = last(A) last(B), last(D(e: z)) = {D(e: z)}, last(A || B) = . Однако если A || B реализуется последовательным исполнением A и B, например, как B; A, то last(A || B) = last(A).

Умн(nat a, b: nat c) { if (a = 0) c = 0 else c = b + Умн(a – 1, b) } не хвостовая рекурсия!!

D(nat a, b: nat c)

{ if (a = b) c = a else if (a < b) D(a, b - a: c) else D(a - b, b: c) } это хвостовая рекурсия

Определение предиката A(x: y) { S }

Вызов A(e: z) с хвостовой рекурсией внутри S. Тогда этот вызов должен иметь вид A(e: y), т.е. z = y.

Подставим определение предиката A на место вызова A(e: y). Получим: |x| = |e|; { S }.

Обозначим через S’ оператор, полученный заменой в S подстановкой определения на место вызова A(e: y). Можно заменить в S’ второе вхождение S передачей управления на начало оператора S’. В итоге определение предиката A преобразуется к виду: A(x: y) { M: S’’ }, где S’’ получается из S

заменой вызова A(e: y) парой операторов: |x| = | e|; goto M.

трансформация замены хвостовой рекурсии циклом.

D(nat a, nat b: nat c) {

M:if (a = b) c = a

else if (a < b) {|a, b| = |a, b - a|; goto M} else {|a, b| = |a - b, b|; goto M}

}

Раскроем групповые операторы присваивания, а также заменим фрагмент с операторами перехода на цикл for. Получим:

D(nat a, nat b: nat c) { for ( ; ; ) {

if (a = b) {c = a; break; } if (a < b) b = b – a

else a = a – b

}

}

Склеивание переменных

Задачи экономии памяти в классических работах А.П. Ершова, С.С. Лаврова, В.В. Мартынюка.

Трансформация склеивания переменных - замена нескольких переменных одной.

a b, c - замена всех вхождений имен b и c на имя a.

Склеиваются переменные одного типа, между которыми имеется информационная связь. В каждом операторе программы определяются аргументы и результаты оператора.

Условия корректности, гарантирующих эквивалентность определений предиката до и после проведения склеивания: переменная a не является пост-аргументом оператора, в котором проводятся склеивания.

Результат оператора может быть склеен с любым аргументом соответствующего типа при условии, что аргумент не используется далее после завершения исполнения оператора.

Метод обобщения исходной задачи

Проблема приведения рекурсии к хвостовому виду.

Техника автоматических преобразований проблематична.

Метод обобщения исходной задачи для получения

решения с хвостовой формой рекурсии.

Умн(nat a, b: nat c) { if (a = 0) c = 0 else c = b + Умн(a – 1, b) }

Прием – использование дополнительного параметра

d качестве накопителя.

Умн1(nat a, b, d: nat c) pre a ≥ 0 & b ≥ 0 & d ≥ 0 !!!

post c = a b + d; Умн(a, b: c) Умн1(a, b, 0: c). Тогда: Умн(nat a, b: nat c)

{ Умн1(a, b, 0: c) } post c = a b;

Умн1(nat a, b, d: nat c)

{ if (a = 0) c = d else Умн1(a – 1, b, d + b: c) } post c = a b + d;

Склеивание c d. Умн1(nat a, b, c: nat c)

{ if (a = 0) c = c else Умн1(a – 1, b, с + b: c) } Замена хвостовой рекурсии циклом: Умн1(nat a, b, c: nat c)

{M: if (a = 0) else {|a, b, c| = |a – 1, b, с + b|; goto M } } Раскрытие группового оператора и оформления цикла:

Умн1(nat a, b, c: nat c) {for (; a != 0; ) {a = a – 1; c = с + b} }

Подстановка определения на место вызова

|a, b, c| = | a, b, 0 |; for (; a != 0; ) {a = a – 1; c = с + b} Раскрытие группового оператора:

c = 0; for (; a != 0; ) {a = a – 1; c = с + b}

Соседние файлы в папке lectures