Дерево на рис.5.2.1 является неупорядоченным.

Рис.4.2.2. Упорядоченное бинарное дерево
Обратите внимание, что упорядочение приводит не к единственному варианту представления множества с помощью дерева. Например, на рис.5.2.3 изображено то же множество, что и на рис.5.2.2.
Будем называть линейным представление такого вида, как на рис.5.2.3, и сбалансированным - такое, как на рис.5.2.2.
Моделирование принадлежности множеству. Имея множество, описанное бинарным деревом, мы можем моделировать принадлежность множеству с помощью целевого утверждения принадлежит_дереву. При этом используется оператор @ <, выражающий отношение «меньше, чем», и оператор @ >, выражающий отношение «больше, чем».
/* Граничное условие: X принадлежит
/* дереву, если X является корнем.
принадлежит_дереву(Х, бд(Лд, Х,Пд)).
/* Рекурсивные условия
/* X принадлежит дереву, если X больше
/* значения корня и находится в правом
/* поддереве:
принадлежит_дереву(Х, бд(Лд, Y,Пд)) :-
X@Y,
принадлежит_дереву (Х, Пд).
/* X принадлежит дереву, если X меньше
/* значения корня и находится в левом
/* поддереве:
принадлежит_дереву(Х, бд(Лд, Y,Пд)):-
X@Y,
принадлежит_дереву (Х, Лд).
Если множество из первых 1024 чисел описать с помощью сбалансированного бинарного дерева Т, то при ответе на запрос
?- принадлежит_дереву(3000,Т).
Пролог сравнит число 3000 не более чем с 11 элементами множества, прежде чем ответит:
нет
Конечно, если Т имеет линейное представление, то потребуется сравнение 3000 с 1024 элементами множества.
Построение бинарного дерева. Задача создания упорядоченного бинарного дерева при добавлении элемента X к другому упорядоченному бинарному дереву формулируется следующим образом:
Граничное условие:
Добавление X к nil дает бд(nil,Х,nil).
Рекурсивные условия:
При добавлении X к бд(Лд, К,Пд) нужно рассмотреть два случая, чтобы быть уверенным, что результирующее дерево будет упорядоченным.
1. X меньше, чем К. В этом случае нужно добавить X к Лд, чтобы получить левое поддерево. Правое поддерево равно Пд, а значение корня результирующего дерева равно К.
2. X больше, чем К. В таком случае нужно добавить X к Пд, чтобы получить правое поддерево. Левое поддерево равно Лд, а значение корня - К.
Такой формулировке задачи соответствует программа:
/* Граничное условие:
включ_бд(nil, Х,бд(nil, Х,nil)).
/* Рекурсивные условия:
/*(1)
включ_бд(бд(Лд, К,Пд),Х, бд(Лднов, К,Пд)) :-
Х@К,
включ_бд(Лд, Х,Лднов).
/*(2)
включ_бд(бд(Лд, К,Пд),Х, бд(Лд, К,Пднов)):-
Х@К,
включ_бд(Пд, Х,Пднов).
На запрос
?- включ_бд(nil, d,T1) ,включ_бд (Т1 ,а, Т2).
будут получены значения
T1=бд(nil, d,nil)
T2=бд(бд(nil, a,nil),d, nil)
Процедуру включ_бд() можно использовать для построения упорядоченного дерева из списка:
/* Граничное условие:
список_в_дерево([],nil).
/* Рекурсивное условие:
список_в_дерево([Н | Т],Бд) :-
список_в_дерево(Т, Бд2),
включ_бд(Н, Бд2,Бд).
Заметим, что включ_бд не обеспечивает построения сбалансированного дерева. Однако существуют алгоритмы, гарантирующие такое построение.
Создание отсортированного списка. Для построения отсортированного списка элементов воспользуемся упорядоченным бинарным деревом.
Граничное условие:
Пустое бинарное дерево (nil) приводит к пустому списку [].
Рекурсивное условие:
Отсортированный список для упорядоченного бинарного дерева бд(Лд, К,Пд), где Лд имеет отсортированный список СЛ, а Пд - отсортированный список СП, получается присоединением [К | СП] к СЛ.
Рассмотрим, например, упорядоченное бинарное дерево
бд(бд(бд(nil, alice, nil),fred, бд(nil, graham, nil)),
jim, бд(nil, ray, nil))
Левому поддереву соответствует отсортированный список
[alice, fred, graham]
Правому поддереву - отсортированный список
[rау]
Следовательно, отсортированным списком для всего дерева будет
присоединить( [alice, fred, graham], [jim | ray] ,L)
что равно
[alice, fred, graham, jim, ray]
Используя процедуру присоединить (см. разд. 5.1.2), преобразуем приведенный алгоритм в утверждения Пролога:
дерево_в_список (nil, []).
дерево_в_список(бд(Лд, К,Пд),С) :-
дерево_в_список(Лд, СЛ),
дерево_в_список(Пд, СЛ),
присоединить (СЛ, [К | СП],С).
5 Операторы и структуры
В этой главе описывается связь операторов со структурами и излагаются способы объявления и использования операторов. Дается объяснение следующим трем свойствам операторов:
1) позиция;
2) приоритет;
3) ассоциативность.
Синтаксис операторов
Структура в Прологе образуется из атома, называемого главным функтором, и следующей за ним последовательности термов, называемых компонентами. Последовательность компонент заключается в круглые скобки. Между главным функтором и открывающей скобкой не должно быть пробела. Компоненту разделяются запятыми. Например, +(1,2) представляет собой структуру с главным функтором + и компонентами 1 и 2.
Для некоторых типов структур допустима более удобная запись с помощью альтернативных форм синтаксиса:
а) синтаксис операторов для структур, арность которых равна одному или двум;
б) синтаксис списков для структур в виде списков;
в) синтаксис строк для списков символов, записанных в кодах ASCII или EBCDIC.
В настоящей главе мы обсудим первую форму - синтаксис операторов. Структуры с арностью, равной одному или двум, могут быть переписаны в синтаксисе операторов посредством объявления главного функтора структуры оператором с одним или двумя аргументами. Компоненты структуры записываются как аргументы оператора.
Например, если + и - объявить системными операторами, то +(1,2) принимает вид 1+2, а-(1) записывается как -1.
Если арность структуры равна единице, оператор может быть объявлен как
а) префиксный оператор. В этом случае оператор записывается перед единственным аргументом ор А;
б) постфиксный оператор. В этом случае оператор записывается после единственного аргумента А ор.
Если арность структуры равна двум, оператор может быть объявлен как инфиксный. В таком случае он записывается между двумя своими аргументами: А ор В.
Например, такие структуры, как
+(X, Y).
;(P, Q). (; обозначает 'или')
< (X, Y).
является_частью(А, В)
прошло(Р).
допускают общепринятую форму записи
X+Y.
P;Q.
X<Y.
А является_частью В.
Р прошло.
Максимальное число компонент в структуре операторного типа равно двум. Следует подчеркнуть, что синтаксис операторов используется вместо обычного формата структур только для удобства.
Свойства операторов
Для точного определения оператора необходимо указать три его свойства: позицию, приоритет и ассоциативность.
Позиция оператора указывает, где он записывается по отношению к своим аргументам.
Приоритет оператора задает порядок выполнения операций в выражении, содержащем более одного оператора.
Ассоциативность оператора показывает, какая операция выполняется первой в выражении, содержащем два или более оператора с одинаковым приоритетом.
Теперь мы поясним значение свойств и их использование в объявлении оператора.
Позиция оператора в выражении определяет его тип. Как было сказано выше, оператор может относиться к одному из трех типов.
Инфиксный - оператор располагается между двумя аргументами, например: X + Y или X < Y .
Префиксный - оператор располагается перед единственным аргументом, например: not X или - X.
Постфиксный - оператор располагается после единственного аргумента, например: X факториал или Р прошло
Каждый оператор имеет приоритетный номер, указываемый при объявлении оператора. Номер является целым числом и обычно '"' находится в пределах от 1 до 1500.
Если выражение содержит более одного оператора, например
X + Y*2,
то возникает неопределенность в порядке выполнения операций. Эта проблема разрешается посредством назначения всем операторам приоритетного номера. Вычисления осуществляются, начиная с оператора, имеющего наименьший номер, и заканчивая оператором с наибольшим номером. В нашем примере X + Y * 2 оператор * имеет более низкий приоритетный номер, чем оператор +. Поэтому, как и следовало ожидать, выражение вычисляется так: X + (Y * 2).
Теоретический приоритетный номер скобок меньше нуля. Выражение, заключенное в скобки, рассматривается как один терм. Скобки могут использоваться для того, чтобы изменить правила установления ассоциативности и сделать синтаксис выражений более четким.
Ассоциативность оператора указывает, как следует интерпретировать выражение, содержащее несколько операторов с одинаковым приоритетом. Покажем на примере необходимость такой информации.
Рассмотрим выражение a/b/с. Результат его вычисления зависит от расстановки скобок:
(а/b)/с или а/(b/с)
Арифметические операции +, -(минус), * и / определены в Прологе как левоассоциативные. Это означает, что они должны иметь слева операторы равного или низшего приоритета, а справа - операторы строго низшего приоритета.
Следовательно, приведенное выше выражение в соответствии с правилом можно вычислить только как (а/b)/с, а не как а/(b/с).
Позицию и ассоциативность оператора удобно задавать с помощью спецификатора. Перечислим виды спецификаторов:
fx для определения постфиксного оператора;
fy
xf для определения префиксного оператора;
yf
xfx для определения инфиксного оператора;
xfy " (правоассоциативногооператора);
yfx " (левоассоциативного оператора);
yfy
В такой форме записи приняты следующие соглашения:
f представляет оператор. Им может быть системный оператор или оператор, объявленный с помощью предиката ор;
х представляет выражение, содержащее операторы только низшего приоритета по отношению к приоритету f;
у представляет выражение, которое может содержать операторы равного или низшего приоритета по отношению к приоритету f.
Если оператор объявлен со спецификатором yfx, то он является левоассоциативным и подвыражение у вычисляется первым. Возьмем в качестве примера
5 + 4*10/5.
Здесь операторы * и / имеют одинаковый приоритет, но, поскольку они определяются в Прологе как левоассоциатииныс, выражение будет вычисляться следующим образом:
У f x
(5+4*10) / 5
(5+(4*10)) / 5
Следовательно, (5+(4*10))/5 равно 9, если использовать правило определения приоритета.
Если оператор объявлен со спецификатором xfy, то он является правоассоциативным и подвыражение у, стоящее справа, вычисляется первым.
Спецификатор yfy недопустим в Прологе, так как выражения, содержащие операторы с такой ассоциативностью, окажутся неопределенными. Если бы * и / были объявлены со спецификаторами yfy, тогда выражение из предыдущего примера 5+4*10/5 можно было бы вычислить либо как прежде
(5+(4*10))/5=9,
либо как
5+(4*(10/5))=13
(суммирование + объявлено с большим приоритетом, чем * или /).
Применение спецификатора xfx в Прологе допустимо. Он используется, например, при объявлении операторов сравнения, таких, как < и >. Обычно трудностей не возникает, так как для операторов < и > в Прологе определяется более высокий приоритет, чем для арифметических операторов. Следовательно, правая и левая части выражения должны быть вычислены до того, как будет произведена попытка выполнить функцию сравнения, например,
5+2 < 6+3
будет вычисляться как
(5+2)<(6+3)
Однако существует возможность построить синтаксически правильное выражение, которое не подчиняется потенциально неопределенным правилам установления приоритета и ассоциативности.
Примером может служить выражение X opr Y opr Z, где оператор орr имеет позицию и ассоциативность xfx.
В тех случаях, когда приоритет и ассоциативность не определяют порядок вычисления операторов, в большинстве реализаций Пролога первым выполняется оператор, стоящий слева.
Поэтому предыдущее выражение будет вычислено следующим образом:
(a opr b) opr c.
Если * > объявлен как левоассоциативный, а < * - как правоассоциативный оператор с помощью управляющих команд (директив)
?-op(60,yfx,*>).
?-op(60,xfy,<*).
то выражение а*>b*>с вычисляется как (a*>b)*>c, a<*b<*c вычисляется как а<*(b<*с) и а<*b*>с вычисляется как (а<*b)*>с по умолчанию.
Некоторые реализации Пролога в таких неясных случаях выдают сообщение об ошибке.
Пролог предоставляет пользователю возможность определять свои собственные операторы. Для этого служит встроенный оператор ор. Функтор ор имеет три аргумента: приоритет, спецификатор и имя функтора:
op(P, S,N)
Здесь Р - приоритет оператора, S - спецификатор, а N - имя атома, который будет использоваться в качестве главного функтора в соответствующей структуре.
Пролог имеет ряд встроенных операторов, которые перечислены в конце главы. Все они определены так, как если бы для каждого объявления было успешно выполнено целевое утверждение
?-op(P, S,N).
Покажем на примере, как можно написать оператор для преобразования температуры по Цельсию в температуру по Фаренгейту. Сначала зададим предикат, который преобразует температуру С (по Цельсию) в температуру F (по Фаренгейту).
(consult) *
/* приглашение пользователю ввести предложение:
?- [user]
/* ввод предложения
:t(C, F) :- F is (C*9/5+32).
: $ /* окончание работы пользователя
Теперь объявим предикат t как инфиксный левоассоциативный оператор с приоритетом 100:
?-op(100,yfx, t).
Если в программе на Прологе возникает необходимость преобразования температуры, то надо только включить целевое утверждение с оператором t и двумя аргументами: переменной С, конкретизированной некоторым целым числом, и неозначенной переменной F.
Например:
tabulate(C, F) :- write(C),tab(3),C t F,
tab(3),write (F).
Обычно в реализации языка Пролог системные операторы, их позиция/ассоциативность и приоритет объявляются следующим образом:
Имя | Спецификатор | Приоритет |
:- | xfx | 1200 |
?- | fx | 1200 |
; | xfy | 1100 |
,xfy | 1000 | |
spy | fx | 900 |
nospy | fx | 900 |
not | fx | 800 |
. | xfy | 750 |
=.. | xfx | 700 |
= | xfx | 700 |
\= | xfx | 700 |
is | xfx | 700 |
=:= | xfx | 700 |
=\= | xfx | 700 |
< | xfx | 700 |
= < | xfx | 700 |
> | xfx | 700 |
> = | xfx | 700 |
== | xfx | 700 |
\== | xfx | 700 |
— | yfx | 500 |
+ | yfx | 500 |
/ | yfx | 400 |
* | yfx | 400 |
div | yfx | 400 |
mod | xfx | 300 |
6 Встроенные предикаты
В настоящей главе рассматриваются встроенные предикаты Пролога, описываются способы обновления базы данных, вопросы ввода/вывода и обработки файлов.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 |


