Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки логического программирования. Учебное пособие
.pdf
конструкции с двумя функциями (тождественной для копирования
исходного кортежа и генератора константы 0):
DEF init :: (id, const(0)).
Теперь определение функции count можно получить, используя
следующую рекурсивную идею: при помощи конструкции уменьшаем на
единицу число неподсчитанных элементов исходного кортежа (хвост
первого элемента вспомогательного кортежа) и увеличиваем на
единицу значение счетчика подсчитанных элементов (специализатор
увеличения на единицу). Это необходимо делать до тех пор, пока
первый элемент вспомогательного кортежа не станет пустым (тогда
будут подсчитаны все элементы исходного кортежа) и в этом случае
вернуть в качестве окончательного значения второй элемент
вспомогательного кортежа. В результате получим следующее описание:
DEF count :: null*1 -> 2; count*(t1*1, (s+1)*2).
Мы получили полное описание функции long. Так, например,
long : <x1, x2, x3> = count:(init:<x1, x2, x3>) =
= count:< <x1, x2, x3>, 0> = count:"x2, x3>, 1> =
= count:< <x3>, 2> = count:">, 3> = 3.
Пример 4. Определим теперь функцию транспонирования матрицы
trans, которую мы ввели ранее как примитивную. Для этого заметим,
что первая строка результата состоит из первых элементов строк
исходной матрицы. Поэтому для ее получения нужно применить
селектор 1 для всех элементов кортежа исходной матрицы, т. е. взять
общую аппликацию этого селектора: A1. Полученную таким образом 1ю строку результирующей матрицы нужно присоединить слева к
результату транспонирования матрицы без первых элементов в строках,
которая получается в результате выборки хвостов для каждой строки, т.
е. опять общей аппликации, но уже для функции хвостов строк: At1.
Поэтому получается следующее решение, использующее рекурсию:
DEF trans :: null -> <>; appendl*(A1, trans*At1).
Продемонстрируем выполнение полученной функции на примере:
Языки логического программированияВ.С. Рублев
111

trans :<< 1; 2; 3 >; < 4; 5; 6 >; < 7; 8; 9 >>=
= appendl :<< 1; 4; 7 >; trans :<< 2; 3 >; < 5; 6 >; < 8; 9 >>>=
= appendl :<< 1; 4; 7 >;
appendl :<< 2; 5; 8 >; trans :<< 3 >; < 6 >; < 9 >>>>=
= appendl :<< 1; 4; 7 >; appendl :<< 2; 5; 8 >;
appendl :<< 3; 6; 9 >; trans :<>>>>=
= appendl :<< 1; 4; 7 >; appendl :<< 2; 5; 8 >;
appendl :<< 3; 6; 9 >; trans :<>>>>=
= appendl :<< 1; 4; 7 >; appendl :<< 2; 5; 8 >;
appendl :<< 3; 6; 9 >; <>>>>=
= appendl :<< 1; 4; 7 >; appendl :<< 2; 5; 8 >; << 3; 6; 9 >>>>=
= appendl :<< 1; 4; 7 >; << 2; 5; 8 >; < 3; 6; 9 >>>=
= appendl :<< 1; 4; 7 >; < 2; 5; 8 >; < 3; 6; 9 >> :
Пример 5. Определим функцию факториала. Обозначим ее как !. Будем
исходить из определения
!n=1, если n=0; иначе равен n* !(n-1)
и проведем пошаговую детализацию (сверху вниз).
DEF ! :: p -> f; g;
где p – условие равенства аргумента нулю, f – функция, возвращающая
1, а g – функция умножения аргумента на значение факториала от
уменьшенного на 1 значения аргумента. Поэтому
DEF p :: eq * f1,
где функция f1 должна создать кортеж из пары – значение аргумента и
0:
DEF f1 :: (id; const(0)).
Теперь функция p полностью определена. Перейдем теперь к функциям
f и g:
DEF f :: const(1),
DEF g :: x * (g1; g2),
Языки логического программированияВ.С. Рублев
112

где g1 есть id, а g2 определяется как композиция факториала и
функции ( s - 1 ), образованной специализатором:
DEF g2 :: ! * (s - 1).
Объединяя все в одну конструкцию определения факториала, получим:
DEF ! :: eq * (id; const(0)) -> const(1); x * (id; ! * (s - 1)):
Продемонстрируем выполнение полученной функции на примере:
! : 3 = eq :< 3; 0 > -> const(1) : 3; x :< 3; ! : - < 3; 1 >>=
= x :< 3; ! : 2 >=
= x :< 3; eq :< 2; 0 >-> const(1) : 2; x :< 2; ! : - < 2; 1 >>=
= x :< 3; x :< 2; ! : 1 >>=
= x :< 3; x :< 2; eq :< 1; 0 >-> const(1) : 1;
x :< 1; ! : - < 1; 1 >>>>=
= x :< 3; x :< 2; x :< 1; ! : 0 >>>=
= x :< 3; x :< 2; x :< 1; eq :< 0; 0 >-> const(1) : 0;
x :< 0; ! : - < 0; 1 >>>>>=
= x :< 3; x :< 2; x :< 1; 1 >>>= x :< 3; x :< 2; 1 >>=
= x :< 3; 2 >= 6:
Пример 6. Скалярное произведение векторов. Исходным данным
является кортеж с двумя элементами - векторами, т. е. также кортежами.
Иными словами, исходные данные это матрица с двумя строкамивекторами. Скалярное произведение - это сумма попарных
произведений одноименных компонент векторов. Поэтому надо сначала
разбить элементы векторов на пары, и это проще всего сделать,
транспонировав исходную матрицу векторов с помощью функции
trans - тогда каждая строка транспонированной матрицы и будет
соответствующей парой элементов. Затем надо для каждой строки
транспонированной матрицы получить произведение, т. е. применить
общую аппликацию произведения A x: получится вектор (кортеж)
попарных произведений. Наконец, надо просуммировать все элементы
полученного вектора, т. е. применить редукцию сложения /+. Таким
образом, скалярное произведение можно определить как композицию:
DEF IP :: (/+)*( A x)*trans.
Языки логического программированияВ.С. Рублев
113

Продемонстрируем выполнение полученной функции на примере:
IP :<< 1; 2; 3 >; < 4; 5; 6 >>=
= /+ : (A x ) : trans :<< 1; 2; 3 >; < 4; 5; 6 >>=
= /+ : (A x ) :<< 1; 4 >; < 2; 5 >; < 3; 6 >>=
= /+ :< 4; 10; 18 >= 32:
В заключение отметим особенность стиля программирования на языке
модели Бэкуса:
нет ничего лишнего, в отличие от языков фон Неймановского
типа, где приходится вводить специальные переменные для
обозначения данных программы, индексов, счетчиков;
программа легко разлагается на понятные части, связанные с
алгоритмом - достигнут предел концептуальной ясности.
Упражнения
1. Определите функцию revers, переворачивающую в обратном
порядке любой кортеж, но не его элементы.
2. Определите функцию allrevers, переворачивающую в
обратном порядке любой кортеж и любые его элементы.
3. Определите функцию NulVect, обнуляющую все элементы
вектора.
4. Определите функцию nul, обнуляющую все элементы любого
объекта.
5. Определите функцию vector, проверяющую, является ли объект
одномерным вектором.
6. Определите функцию matrix, проверяющую, является ли объект
двумерной матрицей.
7. Определите функцию NulDiag, обнуляющую все элементы
диагонали матрицы.
8. Определите функцию Nul1col, обнуляющую все элементы 1-го
столбца матрицы.
9. Определите функцию Nul1line, обнуляющую все элементы 1-й
строки матрицы.
10. Определите функцию NulExDiag, обнуляющую все элементы
матрицы, кроме главной диагонали.
Языки логического программированияВ.С. Рублев
114

11. Определите функцию SqMatrix, проверяющую, является ли
объект квадратной матрицей.
12. Определите функцию Turn90SqMatrix, поворачивающую
квадратную матрицу на 90 градусов по часовой стрелке.
Языки логического программированияВ.С. Рублев
115

Модель Бэкуса алгебры программ: алгебра программ
Алгебра программ: класс функций как область значений алгебры; формы
как операции алгебры; аксиомы алгебры программ и их обоснование;
пример доказательства теорем при помощи алгебры программ.
Алгебра программ
Простота форм модели Бэкуса, заключающаяся в их регулярности и
свободе от контекста, способствует построению системы регулярных
преобразований, сохраняющих смысл программ, и потому названа
Бэкусом алгеброй программ. Область значений этой алгебры - класс
функций в модели Бэкуса, а операциями алгебры являются формы.
Например, f*(g*h) есть формула в алгебре программ, а результат
"вычисления" этого выражения зависит от значений переменных f,\
g,\ h. При их интерпретации это вполне определенные функции.
Например, при интерпретации f -> id, g -> id, h -> id
f*(g*h) = id.
В алгебре программ с программами-выражениями можно обращаться
так же, как в обычной алгебре - с формулами, уравнениями,
неравенствами. И этим умением можно упрощать программы,
доказывать их эквивалентность (значит, и корректность).
Основой алгебры программ является серия законов. Например,
(f, g)*h = ((f*h), (g*h)).
К законам можно относиться как к аксиомам и теоремам. Будем
относиться к нижеследующим 6 законам как к аксиомам.
1. (f1,..., fn)*g = (f1*g,..., fn*g)
2. Af*(g1,..., gn) = (f*g1,..., f*gn)
3. /f*(g1,..., gn) = f*(g1, /f*(g2,..., gn) (n >=
2), /f*g = g
4. (f1*1,..., fn*n)*(g1,..., gn) = (f1*g1,...,
fn*gn)
Языки логического программированияВ.С. Рублев
116

5. appendl*(f*g, Af*h) = Af*appendl*(g, h)
6. A(f*g) = Af*Ag
Для обоснования той или иной аксиомы достаточно показать, что
применение к любым данным формы в левой части равенства
совпадает с применением к этим же данным формы в правой части
равенства. Обоснуем для примера аксиому 1o. Для любых данных x
(атом, неопределенность <?> или непустой кортеж):
(f1,..., fn)*g : x =(f1,..., fn) : (g : x) =
= <f1 : (g : x),..., fn : (g : x)> = <f1*g : x,..., fn*g : x> =
= (f_1*g,..., f_n*g) : x, что и требовалось. Обоснования
остальных аксиом мы оставляем читателю.
В формулировках теорем принято продукцию обозначать двойной
стрелкой ( ). Приведем и докажем теорему, которая нам потребуется
в дальнейшем.
Теорема 1.
pair & not*null*1 => appendl*((1*1, 2), distr*(t1*1, 2)) = distr
Условие теоремы читается так:
Верно следующее равенство форм для любых аргументов,
представляющих собой кортеж из 2 элементов с первым непустым
элементом:
appendl*((1*1, 2), distr*(t1*1, 2)) = distr.
Доказательство.
Рассмотрим 2 случая:
1. 1-й элемент кортежа-аргумента x - атом или неопределенность
<?>.
2. 1-й элемент кортежа-аргумента x - любой непустой кортеж.
Языки логического программированияВ.С. Рублев
117

1. distr : <x, y> = <?> (из определения функции distr ).
t1*1:<x, y> =<?> (из определения функции distr ).
Поэтому и правая, и левая форма равенства дают
неопределенность <?>.
2. x=<x1,..., xn> (n >= 1).
appendl*((1*1, 2), distr*(t1*1, 2)) : <x, y> =
= appendl*((1*1, 2):<x, y>, distr*(t1*1, 2):<x, y>) =
= appendl*<<1:x, y>, distr:<t1:x, y>>) =
2a) Если t1:x = <>, то
= appendl:<<x1, y>, <>> = <<x1, y>> =
= distr:<x, y>.
2b) Если , то
= appendl:<<x1, y>, <<x2, y>,..., <xn, y>>> =
= distr:<x, y>.
Это и требовалось доказать.
Рассмотрим теперь пример построения программы умножения матриц
и преобразования этой программы с целью ее улучшения.
Упражнения
1. Обоснуйте все аксиомы алгебры программ.
2. Является ли верным следующее утверждение:
revers*revers=id, где revers - функция, определенная в
упражнении 4.8.1?
Языки логического программированияВ.С. Рублев
118

Модель Бэкуса алгебры программ: умножение матриц и
алгебраическое преобразование программы
Программа умножения матриц: пошаговая разработка программы;
анализ программы с целью оптимизации; оптимизация программы и
доказательство ее корректности с помощью алгебры программ.
Программа умножения матриц и ее алгебраическое
преобразование
В качестве примера мощи введенной алгебры программ мы рассмотрим
построение программы перемножения матриц. Сначала создадим
простую программу, не задумываясь о ее эффективности. Затем
построим более сложную, но эффективную программу, и докажем ее
корректность, используя аппарат алгебры программ.
Напомним, что умножение матрицы размерности на
матрицу размерности дает результирующую матрицу
размерности каждый элемент которой получается как
скалярное произведение -й строки матрицы на -й столбец
матрицы Поэтому нужно образовать матрицу пар, где на месте
каждого элемента результирующей матрицы находится кортеж пары
из соответствующей строки первой исходной матрицы и
соответствующего столбца - второй. Поэтому мы можем определить
пока функцию перемножения матриц таким образом:
где функция готовит матрицу пар, а функция производит
скалярное умножение для каждой пары. Таким образом, мы разбили
весь алгоритм на две части.
Для лучшего понимания процесса разработки программы мы будем
демонстрировать отдельные части алгоритма на следующем примере
исходных данных:
Языки логического программированияВ.С. Рублев
119

В этом примере и
Матрица пар должна выглядеть следующим кортежем:
Для каждой пары функция должна выполнить следующую функцию
:
которая должна вычислить скалярное произведение:
Поэтому функция определяется следующим образом (двойная общая
аппликация):
и заменяет кортеж матрицы пар на следующий кортеж
который соответствует результирующей матрице
Таким образом, функция определена полностью.
Для определения функции готовящей матрицу пар, необходимо
применить функции расписывания и Но так как каждая
строка 1-й матрицы сочетается с каждым столбцом второй матрицы, то
мы предварительно расположим 2-ю матрицу по столбцам. Таким
образом мы разобьем алгоритм также на две части:
Языки логического программированияВ.С. Рублев
120
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
