- •ОгЛавление
- •8 Литература 93
- •Лабораторная работа №1 Знакомство с языком логического программирования Пролог
- •1.1 Цель работы
- •1.2 Теоретические сведения
- •1.3 Среда VisualProlog
- •1.4 Выполнение работы
- •1.5 Задание на лабораторную работу
- •2.3 Выполнение работы
- •2.4 Задание на лабораторную работу
- •2.5 Контрольные вопросы
- •Лабораторная работа №3 Рекурсивные структуры данных
- •3.1 Цель работы
- •3.2 Теоретические сведения
- •3.3 Выполнение работы
- •3.4 Задание на лабораторную работу
- •3.5 Контрольные вопросы
- •Лабораторная работа №4 Знакомство с языком списочных структур Лисп
- •4.1 Цель работы
- •4.2 Теоретические сведения
- •4.3 Среда FreeLisp
- •4.4 Выполнение работы
- •4.5 Задание на лабораторную работу
- •4.6 Контрольные вопросы
- •Лабораторная работа №5 Рекурсия и итерация в языке Лисп
- •5.1 Цель работы
- •5.2 Теоретические сведения
- •5.3 Выполнение работы
- •5.4 Задание на лабораторную работу
- •5. 5. Контрольные вопросы
- •6.2.2 Замыкания
- •6.2.3 Макросы
- •6.3 Задание на лабораторную работу
- •6.4 Контрольные вопросы
- •Лабораторная работа №7 Методы поиска решений в пространствах состояний
- •7.1 Цель работы
- •7.2 Теоретические сведения
- •7.4 Выполнение работы
- •7.5 Задание на лабораторную работу
- •7.6 Контрольные вопросы
- •8 Литература
3.3 Выполнение работы
Рекурсивные алгоритмы как нельзя лучше подходят для обработки рекурсивных структур данных.
Прохождение бинарных деревьев
Рассмотрим алгоритм обхода бинарного дерева в симметричном порядке. Этот алгоритм рекурсивен по своей природе и сводится к следующим шагам:
Пройти в симметричном порядке левое поддерево.
Попасть в корень.
Пройти в симметричном порядке правое поддерево.
На языке Паскаль этот алгоритм можно записать следующим образом:
type TPNode = ^Tree;
Tree = record
Element: string;
Left, Right: TPNode;
end;
procedure PBDSP (PTree: TPNode);
begin
if PTree <> Nil then
begin PBDSP (PTree^.Left);
Writeln (PTree^.Element);
PBDSP (PTree^.Right)
end
end;
На языке Пролог симметричный порядок обхода дерева можно представить так:
traverse(empty).
traverse(
tree(Element, Left, Right)):-traverse(Left),
write(Element), nl,
traverse(Right).
Обработка списков
В качестве примера работы со списками рассмотрим процедуру поиска элемента в списке. Поиск представляет собой просмотр списка на предмет выявления соответствия между элементом данных (объектом поиска) и элементом просматриваемого списка. Если такое соответствие найдено, то поиск заканчивается успехом. В противном случае поиск заканчивается неуспехом. Элемент принадлежит списку, если выполняется одно из условий:
Элемент совпадает с головой списка.
Элемент принадлежит хвосту списка.
На языке Паскаль алгоритм поиска элемента в списке можно реализовать следующим образом:
type TList = ^TElemList;
TElemList = record;
Inf: char;
Next: TList;.
end;
function Find(Elem:Сhar; List:TList):boolean;
begin
{Проверяется случай, когда список пуст}
if (List = Nil) then Find:=false
{Рассматривается случай, когда список не пуст}
else {искомый элемент совпадает с головой списка}
if List^.Inf = Elem then Find := true
{иначе ищем элемент в хвосте списка}
else Find := Find(Elem, List^.Next);
end;
Функция Find получает два аргумента: искомый элемент и список, в котором производится поиск.
На языке Пролог алгоритм поиска элемента в списке можно реализовать следующим образом:
domains
list = element*
element = symbol
predicates
find(element, list)
clauses
%искомый элемент совпадает с головой списка
find(Elem, [Elem|_]).
%иначе ищем элемент в хвосте списка
find(Elem, [_|Tail]):-find(Elem, Tail).
?- find(“3”, [“1”’, “3”, “4”])
Yes
При задании целевого утверждения Пролог попытается вначале применить первое правило. Если искомый элемент не совпадает с первым элементом списка (головой списка), то делается откат и Пролог пытается для доказательства целевого утверждения применить второе правило. Пролог унифицирует имеющиеся термы с заголовком второго правила find(Elem, [_|Tail]). При этом первый элемент списка ставится в соответствие анонимной переменной. Так делается потому, что значение первого элемента списка не представляет для нас интереса (второе правило не было бы задействовано, если бы первый элемент списка совпадал с объектом поиска – первое правило). Теперь мы хотим присвоить переменной Tail хвост списка, чтобы Пролог попытался установить соответствие между объектом поиска и головой хвоста списка. Попытка согласовать подцель find(Elem, Tail) заставляет Пролог представить хвост текущего списка как новый самостоятельный список. Этот процесс повторяется до тех пор, пока либо выполнится первое правило (успешное завершение программы), либо пока не исчерпается весь список (неуспешное завершение программы). Так при выполнении целевого утверждения find(“3”, [“1”, “3”, “4”]) получим следующую последовательность рекурсивных вызовов:
?- find(“3”, [“1”, “3”, “4”])
не подходит, так как искомый элемент не
совпадает с головой списка
Elem=3, Tail=[“3”, “4”]
?- find(“3”, [“3”, “4”])
Elem=“3” совпадает с головой списка
ответ Yes
Следует отметить, что реализация алгоритма поиска элемента в списке на языке Пролог не совсем эквивалентна реализации того же алгоритма на языке Паскаль. Отличие состоит в том, что в традиционных языках программирования с вычислением функции неразрывно связано направление: функция получает аргументы и возвращает значение. То же самое изображение функции не позволяет в «перевернутом направлении» вычислять значения аргументов исходя из значения функции. Эта несимметричность проистекает из того, что написанные на традиционных языках программы, обрабатывают данные в порядке, задаваемом описанием алгоритма. В логических языках алгоритмы в таком смысле не используются. Здесь решение получают из описания структуры и условий задачи. Такие языки называют декларативными. Поскольку в декларативной программе последовательность и способ выполнения программы не фиксируются, как при описании алгоритма, программы могут в принципе работать в обоих направлениях. Т.е. написанная на языке Пролог программа может на основе исходных данных вычислить результат, но и с тем же успехом без дополнительного программирования на основе результата – исходные данные.
Например, в результате выполнения целевого утверждения
?- find(X, [“1”, “3”, “4”])
получим следующие значения переменной X:
X = “1”
X = “3”
X = “4”
