2 Синтаксис и унификация

реклама
Методические указания по языку «Пролог»
Составил Новожилов С. А.
Содержание
Введение ....................................................................................................................... 3
1 Синтаксис и унификация ......................................................................................... 3
1.1 Синтаксис ........................................................................................................... 3
Термы ..................................................................................................................... 3
Константы ............................................................................................................. 4
Атом ....................................................................................................................... 4
Числа ...................................................................................................................... 5
Переменные........................................................................................................... 5
Область действия переменных............................................................................ 5
Сложные термы или структуры .......................................................................... 5
Синтаксис операторов.......................................................................................... 6
Синтаксис списков ............................................................................................... 6
Синтаксис строк.................................................................................................... 6
Утверждения ......................................................................................................... 6
Запросы .................................................................................................................. 8
Ввод программ ...................................................................................................... 9
1.2 Унификация ...................................................................................................... 10
2 Арифметические выражения................................................................................. 12
2.1 Введение ........................................................................................................... 12
2.2. Арифметические выражения ......................................................................... 13
2.3 Арифметические операторы ........................................................................... 14
2.4 Вычисление арифметических выражений .................................................... 15
2.5 Сравнение результатов арифметических выражений .................................. 15
3 Рекурсия .................................................................................................................. 16
3.1 Стратегия «разделяй и властвуй» .................................................................. 16
Фаза разбиения ................................................................................................... 18
Фаза решения задачи .......................................................................................... 20
3.2 Восходящая стратегия ..................................................................................... 22
3.3 Рекурсия и эффективность .............................................................................. 24
4 Структура данных .................................................................................................. 29
4.1 Списки ............................................................................................................... 29
Списковая форма записи ................................................................................... 29
Некоторые стандартные целевые утверждения для обработки списков ...... 30
4.2.Бинарные деревья ............................................................................................ 38
Представление бинарных деревьев .................................................................. 38
Представление множеств с помощью бинарных деревьев ............................ 39
5 Операторы и структуры ......................................................................................... 43
5.1 Операторы и структуры .................................................................................. 43
Синтаксис операторов........................................................................................ 43
1
Свойства операторов .......................................................................................... 45
5.2 Позиция операторов ........................................................................................ 45
5.3 Приоритет операторов ..................................................................................... 45
5.4 Ассоциативность операторов ......................................................................... 46
5.5 Спецификаторы ................................................................................................ 46
5.6 Операторы объявления .................................................................................... 48
5.7 Системные операторы ..................................................................................... 49
6 Встроенные предикаты .......................................................................................... 50
6.1 Встроенные предикаты ................................................................................... 50
6.2 Обновление базы данных Пролога ................................................................ 50
Добавление и удаление утверждений .............................................................. 51
Считывание утверждений в базу данных ........................................................ 52
Предикаты сохранения и восстановления ....................................................... 53
6.3 Особенности ввода и вывода .......................................................................... 54
Чтение символов ................................................................................................. 54
Запись символов ................................................................................................. 55
Считывание термов ............................................................................................ 55
Запись термов ..................................................................................................... 57
6.4. Обработка файлов ........................................................................................... 58
Редактирование программ на Прологе ............................................................. 60
Распечатка предикатов....................................................................................... 60
7 Отладка .................................................................................................................... 61
7.1.Трассировка ...................................................................................................... 61
Включение и выключение механизма трассировки ....................................... 63
Необязательные параметры трассировки ........................................................ 63
Предикаты трассировки ..................................................................................... 64
Режимы трассировки .......................................................................................... 64
7.2. Установка контрольных точек....................................................................... 64
Возможные действия в контрольной точке ..................................................... 64
Включение и выключение режима контрольных точек ................................. 65
Необязательные параметры режима контрольных точек .............................. 65
Предикаты для работы с контрольными точками........................................... 66
Режимы, связанные с контрольными точками ................................................ 66
Основные отладочные предикаты .................................................................... 66
Статистическая информация ............................................................................. 66
Приложение A(Синтаксис)....................................................................................... 67
А.1. Синтаксис программ ...................................................................................... 68
А.2. Синтаксис термов........................................................................................... 68
А.З. Синтаксис списка ........................................................................................... 70
А.4. Синтаксис комментария ................................................................................ 70
Приложение Б(Встроенные операторы) ................................................................. 70
Предметный указатель .............................................................................................. 76
2
Введение
В начале 70-х годов группа специалистов Марсельского университета во
главе с А. Колмероэ разработала специализированную систему для
доказательств теорем. Система доказательств теорем, названная Пролог,
воплощала процедурную семантику Р. Ковальского
Сегодня Пролог – язык, предназначенный для программирования
приложений, использующих средства и методы искусственного интеллекта и
создания экспертных систем.
1 Синтаксис и унификация
1.1 Синтаксис
Термы
Объекты данных в Прологе называют термами. Терм может быть
константой, переменной или составным термом (структурой). Константами
являются целые действительные числа, например:
0,-1,123.4, 0.23Е-5.
К константам относятся также атомы, такие, как:
Голди, а, атом,+,:,’Фред Блогс’,[].
Атом есть любая последовательность символов, заключенная в
одинарные кавычки. Кавычки опускаются, если и без них атом можно отличить
от символов, используемых для обозначения переменных. Приведем несколько
примеров атомов:
Abcd, фред, ‘:’, Джо.
Как ив других языках программирования, константы обозначают
конкретные элементарные объекты, а все другие типы данных в Прологе
составлены из сочетаний констант и переменных.
Имена переменных начинаются с заглавных букв или с символа
подчеркивания «_». Примеры переменных:
Х, Переменная, _3, _переменная.
3
Если переменная используется только один раз, необязательно называть
ее. Она может быть записана как анонимная переменная, состоящая из одного
символа подчеркивания «_». Переменные, подобно атомам, являются
элементарными объектами языка Пролог.
Завершает список синтаксических единиц сложный терм, или структура.
Все, что не может быть отнесено к переменной или константе, называется
сложным термом. Следовательно, сложный терм состоит из констант и
переменных.
Теперь перейдем к более детальному описанию термов.
Константы
Константы известны всем программистам. В Прологе константа может
быть атомом или числом.
Атом
Атом представляет собой произвольную последовательность символов,
заключенную в одинарные кавычки. Одинарный символ кавычки,
встречающийся внутри атома, записывается дважды. Когда атом выводится на
печать, внешние символы кавычек обычно не печатаются. Существует
несколько исключений, когда атомы необязательно записывать в кавычках. Вот
эти исключения:
1) атом, состоящий только из чисел, букв и символа подчеркивания и
начинающийся со строчной буквы;
2) атом, состоящий целиком из специальных символов. К
специальным символам относятся:
+_*/^=~:;?@$&
Заметим, что атом, начинающийся с /*, будет воспринят как начало
комментария, если он не заключен в одинарные кавычки.
Атом, который необязательно заключать в кавычки, может быть записан
и в кавычках. Запись с внешними кавычками и без них определяет один и тот
же атом.
Допустимы случаи, когда атом не содержит ни одного символа (так
называемый ‘нулевой атом’) или содержит непечатаемые символы. При выводе
таких атомов на печать могут возникнуть ошибки.
4
Числа
Большинство реализаций Пролога поддерживает целые и действительные
числа. Для того чтобы выяснить, каковы диапазоны и точность чисел следует
обратиться к руководству по конкретной реализации.
Переменные
Понятие переменной в Прологе отличается от принятого во многих
языках программирования. Переменная не рассматривается как выделенный
участок памяти. Она служит для обозначения объекта, на который нельзя
сослаться по имени. Переменную можно считать локальным именем для
некоторого объекта.
Синтаксис переменной довольно прост. Она должна начинаться с
прописной буквы или символа подчеркивания и содержать только символы
букв, цифр и подчеркивания.
Переменная, состоящая только из символа подчеркивания, называется
анонимной и используется в том случае, когда имя переменной несущественно.
Область действия переменных
Областью действия переменных является утверждение. В пределах
утверждения одно и тоже имя принадлежит одной и той же переменной. Два
утверждения могут использовать одно имя переменной совершенно различным
образом. Правило определения области действия переменной справедливо
также в случае рекурсии и в том случае, когда несколько утверждений имеют
одну и ту же головную цель.
Единственным исключением из правила определения области действия
переменных является анонимная переменная, например, «_»
в цели
любит(Х,_). Каждая анонимная переменная есть отдельная сущность. Она
применяется тогда, когда конкретное значение переменной несущественно для
данного утверждения. Таким образом, каждая анонимная переменная четко
отличается от всех других анонимных переменных в утверждении.
Переменные, отличные от анонимных, называются именованными, а
неконкретизированные(переменные, которым не было присвоено значение)
называют свободными.
Сложные термы или структуры
Структура состоит из атома, называемого главным функтором, и
последовательности
термов,
называемых
компонентами
структуры.
Компоненты разделяются запятыми и заключаются в круглые скобки.
Приведем примеры структурированных термов:
собака (рекс) ,родитель(Х,Y).
5
Число компонент в структуре называется арностью структуры. Так, в
данном примере структура собака имеет арность 1 (записывается как
собака/1), а структура родитель - арность 2 (родитель/2). Заметим, что атом
можно рассматривать как структуру арности 0.
Для некоторых типов структур допустимо использование альтернативных
форм синтаксиса. Это синтаксис операторов для структур арности 1 и 2,
синтаксис списков для структур в форме списков и синтаксис строк для
структур, являющихся списками кодов символов.
Синтаксис операторов
Структуры арности 1 и 2 могут быть записаны в операторной форме, если
атом, используемый как главный функтор в структуре, объявить оператором
(см. гл. 6).
Синтаксис списков
В сущности, список есть не что иное, как некоторая структура арности 2.
Данная структура становится интересной и чрезвычайно полезной в случае,
когда вторая компонента тоже является списком. Вследствие важности таких
структур в Прологе имеются специальные средства для записи списков.
Возможности обработки списков рассматриваются в разд. 5.1.
Синтаксис строк
Строка определяется как список кодов символов. Коды символов имеют
особое значение в языках программирования. Они выступают как средство
связи компьютера с внешним миром. В большинстве реализаций Пролога
существует специальный синтаксис для записи строк. Он подобен синтаксису
атомов. Строкой является любая последовательность символов, которые могут
быть напечатаны (кроме двойных кавычек), заключенная в двойные кавычки.
Двойные кавычки в пределах строки записываются дважды «».
В некоторых реализациях Пролога строки рассматриваются как
определенный тип объектов подобно атомам или спискам. Для их обработки
вводятся специальные встроенные предикаты. В других реализациях строки
обрабатываются в точности так же, как списки, при этом используются
встроенные предикаты для обработки списков. Поскольку все строки могут
быть определены как атомы или как списки целых чисел, и понятие строки
является чисто синтаксическим, мы не будем более к нему возвращаться.
Утверждения
Программа на Прологе есть совокупность утверждений. Утверждения
состоят из целей и хранятся в базе данных Пролога. Таким образом, база
6
данных Пролога может рассматриваться как программа на Прологе. В конце
утверждения ставится точка «.». Иногда утверждение называется
предложением.
Основная операция Пролога - доказательство целей, входящих в
утверждение.
Существуют два типа утверждений:
факт: это одиночная цель, которая, безусловно, истинна;
правило: состоит из одной головной цели и одной или более хвостовых
целей, которые истинны при некоторых условиях.
Правило обычно имеет несколько хвостовых целей в форме конъюнкции
целей.
Конъюнкцию можно рассматривать как логическую функцию И. Таким
образом, правило согласовано, если согласованы все его хвостовые цели.
Примеры фактов:
собака(рекс).
родитель(голди,рекс).
Примеры правил:
собака(X) :- родитель(X,Y) ,собака(Y).
человек(Х) :-мужчина(X).
Разница между правилами и фактами чисто семантическая. Хотя для
правил мы используем синтаксис операторов (см. гл. 6), нет никакого
синтаксического различия между правилом и фактом.
Так, правило
собака (X) :- родитель (X.Y),собака (Y).
может быть задано как
:-собака(Х) ',' родитель(Х,Y),собака(Y).
Запись верна, поскольку :- является оператором «при условии, что», а ',' это оператор конъюнкции. Однако удобнее записывать это как
собака(Х) :-родитель(Х, Y).собака(Y).
и читать следующим образом: «X - собака при условии, что родителем X
является Y и Y - собака».
7
Структуру иногда изображают в виде дерева, число ветвей которого
равно арности структуры.
Рисунок 1.1
Запросы
После записи утверждений в базу данных вычисления могут быть
инициированы вводом запроса.
Запрос выглядит так же, как и целевое утверждение, образуется и
обрабатывается по тем же правилам, но он не входит в базу данных
(программу). В Прологе вычислительная часть программы и данные имеют
одинаковый синтаксис. Программа обладает как декларативной, так w
процедурной семантикой. Мы отложим обсуждение этого вопроса до
последующих глав. Запрос обозначается в Прологе утверждением ?-, имеющим
арность 1. Обычно запрос записывается в операторной форме: за знаком ?следует ряд хвостовых целевых утверждений (чаще всего в виде конъюнкции).
Приведем примеры запросов:
?-собака(X).
?- родитель(Х.У) .собака(Y).
или, иначе,
‘?-‘(собака(Х))
(‘?-‘) ‘,’ (родитель (X,Y) ,собака (Y)).
Последняя запись неудобна тем, что разделитель аргументов в структуре
совпадает с символом конъюнкции. Программисту нужно помнить о различных
значениях символа ','.
Запрос иногда называют управляющей командой (директивой), так как он
требует от Пролог-системы выполнения некоторых действий. Во многих
реализациях Пролога для управляющей команды используется альтернативный
символ, а символ ?- обозначает приглашение верхнего уровня интерпретатора
Пролога. Альтернативным символом является :-. Таким образом,
8
:- write (собака).
- это управляющая команда, в результате выполнения которой печатается атом
собака. Управляющие команды будут рассмотрены ниже при описании ввода
программ.
Ввод программ
Введение списка утверждений в Пролог-систему осуществляется с
помощью встроенного предиката consult. Аргументом предиката consult
является атом, который обычно интерпретируется системой как имя файла,
содержащего текст программы на Прологе. Файл открывается, и его
содержимое записывается в базу данных. Если в файле встречаются
управляющие команды, они сразу же выполняются. Возможен случай, когда
файл не содержит ничего, кроме управляющих команд для загрузки других
файлов. Для ввода утверждений с терминала в большинстве реализаций
Пролога имеется специальный атом, обычно user. С его помощью утверждения
записываются в базу данных, а управляющие команды выполняются немедленно.
Помимо предиката consult, в Прологе существует предикат reconsult. Он
работает аналогичным образом. Но перед добавлением утверждений к базе
данных из нее автоматически удаляются те утверждения, головные цели
которых сопоставимы с целями, содержащимися в файле перезагрузки. Такой
механизм позволяет вводить изменения в базу данных. В Прологе имеются и
другие методы добавления и удаления утверждений из базы данных. Некоторые
реализации языка поддерживают модульную структуру, позволяющую
разрабатывать модульные программы. Эти методы мы обсудим позже.
В заключение раздела дадим формальное определение синтаксиса
Пролога, используя форму записи Бэкуса-Наура, иногда называемую
бэкусовской нормальной формой (БНФ).
запрос ::= голова утверждения
правило ::= голова утверждения :- хвост утверждения
факт ::=
голова утверждения голова утверждения ::= атом | структура
хвост утверждения ::= атом структура,
термы ::=терм [,термы]
терм ::= число | переменная | атом | структура
структура ::= атом (термы)
Данное определение синтаксиса не включает операторную, списковую и
9
строковую формы записи. Полное определение дано в приложении А. Однако
любая программа на Прологе может быть написана с использованием
вышеприведенного синтаксиса. Специальные формы только упрощают
понимание программы. Как мы видим, синтаксис Пролога не требует
пространного объяснения. Но для написания хороших программ необходимо
глубокое понимание языка.
1.2 Унификация
Одним из наиболее важных аспектов программирования на Прологе
являются понятия унификации (отождествления) и конкретизации переменных.
Пролог пытается отождествить термы при доказательстве, или
согласовании, целевого утверждения. Например, в программе из гл. 1 для
согласования запроса ?- собака(Х) целевое утверждение собака(X) было
отождествлено с фактом собака(рекс), в результате чего переменная X стала
конкретизированной: Х= рекс.
Переменные, входящие в утверждения, отождествляются особым образом
- сопоставляются. Факт доказывается для всех значений переменной
(переменных). Правило доказывается для всех значений переменных в
головном целевом утверждении при условии, что хвостовые целевые
утверждения доказаны. Предполагается, что переменные в фактах и головных
целевых утверждениях связаны квантором всеобщности. Переменные
принимают конкретные значения на время доказательства целевого
утверждения.
В том случае, когда переменные содержатся только в хвостовых целевых
утверждениях, правило считается доказанным, если хвостовое целевое
утверждение истинно для одного или более значений переменных.
Переменные, содержащиеся только в хвостовых целевых утверждениях,
связаны квантором существования. Таким образом, они принимают
конкретные значения на то время, когда целевое утверждение, в котором
переменные были согласованы, остается доказанным.
Терм X сопоставляется с термом Y по следующим правилам. Если X и Y константы, то они сопоставимы, только если они одинаковы. Если X является
константой или структурой, a Y - неконкретизированной переменной, то X и Y
сопоставимы и Y принимает значение X (и наоборот). Если X и Y - структуры,
то они сопоставимы тогда и только тогда, когда у них одни и те же главный
функтор и арность и каждая из их соответствующих компонент сопоставима.
Если X и Y - неконкретизированные (свободные) переменные, то они
сопоставимы, в этом случае говорят, что они сцеплены. На рис.2.1 приведены
примеры отождествимых и неотождествимых термов.
10
Терм1
Терм2
Отождествимы ?
джек(Х)
джек (личность)
джек(Х,Х)
джек(Х,Х)
джек(_,_ )
f(Y,Z)
X
джек (человек)
джек (человек)
джек (23,23)
джек(12,23)
джек (12,23)
X
Z
да: Х=человек
нет
да: Х=23
нет
да
да: X=f (Y,Z)
да: X=Z
Рисунок 2.1. Иллюстрация унификации
Заметим, что Пролог находит наиболее общий унификатор термов. В
последнем примере (рис.2.1) существует бесконечное число унификаторов:
X=l, Z=2, X=2, Z-2;...,
но Пролог находит наиболее общий: X=Z.
Следует сказать, что в большинстве реализаций Пролога для повышения
эффективности его работы допускается существование циклических
унификаторов. Например, попытка отождествить термы f(X) и X приведет к
циклическому унификатору X=f(X), который определяет бесконечный терм
f(f(f(f(f(...))))). В программе это иногда вызывает бесконечный цикл.
Возможность отождествления двух термов проверяется с помощью
оператора =.
Ответом на запрос
?- 3+2=5.
Будет
нет
гак как термы не отождествимы (оператор не вычисляет значения своих
аргументов), но попытка доказать
?- строка (поз (X)) = строка (поз(23)).
закончится успехом при
X = 23.
11
Унификация часто используется для доступа к подкомпонентам термов.
Так, в вышеприведенном примере X конкретизируется первой компонентой
терма поз (23), который в свою очередь является компонентой терма строка.
Бывают случаи, когда надо проверить, идентичны ли два терма.
Выполнение оператора = = заканчивается успехом, если его аргументы идентичные термы. Следовательно, запрос
?- строка (поз (X)) ==строка (поз (23)).
дает ответ
нет
поскольку подтерм X в левой части (X - свободная переменная) не идентичен
подтерму 23 в правой части. Однако запрос
?- строка (поз (23)) == строка (поз (23)).
дает ответ
да
Отрицания операторов = и = = записываются как \= и \= = соответственно.
2 Арифметические выражения
В этой главе показано, каким образом Пролог выполняет арифметические
операции. Будут описаны арифметические операторы и их использование в
выражениях, а также рассмотрены встроенные предикаты, служащие для
вычисления и сравнения арифметических выражений.
2.1 Введение
Язык Пролог не предназначен для программирования задач с большим
количеством арифметических операций. Для этого используются процедурные
языки программирования. Однако в любую Пролог-систему включаются все
обычные арифметические операторы:
+
*
/
mod
сложение
вычитание
умножение
деление
остаток от деления целых чисел
12
div
целочисленное деление
В некоторых реализациях языка Пролог присутствует более широкий
набор встроенных арифметических операторов.
Пролог позволяет также сравнивать арифметические выражения,
используя следующие встроенные предикаты:
=:=, =\=, , , =,=
Диапазоны чисел, входящих в арифметические выражения, зависят от
реализации Пролога. Например, система ICLPROLOG оперирует с целыми
числами со знаком в диапазоне
8388606 ... 8388607
В настоящей книге мы предполагаем, что в Пролог-системе реализована
арифметика с плавающей точкой.
2.2. Арифметические выражения
Арифметическое выражение является числом или структурой. В
структуру может входить одна или более компонент, таких, как числа,
арифметические
операторы,
арифметические
списковые
выражения,
переменная, конкретизированная арифметическим выражением, унарные
функторы, функторы преобразования и арифметические функторы.
Числа. Числа и их диапазоны определяются в конкретной реализации
Пролога.
Арифметические операторы. + - * / mod div
Арифметические списковые выражения. Если X - арифметическое
выражение, то список [X] также является арифметическим выражением,
например [1,2,3]. Первый элемент списка используется как операнд в
выражении. Скажем,
Xis([l,2,3]+5)
имеет значение 6.
Арифметические списковые выражения полезны и при обработке
символов, поскольку последние могут рассматриваться как небольшие целые
числа. Например, символ «а» эквивалентен [97] и, будучи использован в
выражении, вычисляется как 97. Поэтому значение выражения «р»+"А"-"а"
равно 80, что соответствует коду ASCII для
Переменная, конкретизированная арифметическим выражением.
13
Примеры:
Х=5+2 и Y=3*(2+А)
Унарные функторы. Примеры:
+(X) и –(Y)
Функторы преобразования. В некоторых реализациях Пролога имеется
арифметика с плавающей точкой, а следовательно, и функторы преобразования.
Например:
float(X) преобразует целое число X в число с плавающей точкой.
Математические функторы. Пример: квадрат(X) объявлен как оператор
и эквивалентен арифметическому выражению (Х*Х).
2.3 Арифметические операторы
Атомы +,-,*,/, mod и div - обычные атомы Пролога и могут
использоваться почти в любом контексте. Указанные атомы - не встроенные
предикаты, а функторы, имеющие силу только в пределах арифметических
выражений. Они определены как инфиксные операторы. Эти атомы являются
главными функторами в структуре, а сама структура может принимать только
описанные выше формы.
Позиция, приоритет и ассоциативность арифметических операторов четко
заданы и перечислены в таблице операторов в гл. 6.
Арифметический оператор выполняется следующим образом. Во-первых,
вычисляются арифметические выражения по обе стороны оператора. Вовторых, над результатом вычислений выполняется нужная операция.
Арифметические операторы определяются Пролог-системой. Если мы
напишем предикат
среднее (X,Y,Z) :- Z is (X+Y)/2.
то хотя можно определить среднее как оператор
?- ор(250,хfх,среднее).
но Пролог выдаст сообщение об ошибке, если встретит выражение
Z is X среднее Y.
Это произойдет потому, что X среднее Y не образует арифметического
14
выражения, а среднее не является арифметическим оператором, определенным
в системе.
2.4 Вычисление арифметических выражений
В Прологе не допускаются присваивания вида
Сумма =2 + 4.
Выражение такого типа вычисляется только с помощью системного
предиката is, например:
Сумма is 2 + 4.
Предикат is определен как инфиксный оператор. Его левый аргумент или число, или неконкретизированная переменная, а правый аргумент арифметическое выражение.
Попытка доказательства целевого утверждения X is Y заканчивается
успехом в одном из следующих случаев:
а) X - неконкретизированная переменная, а результат вычисления
выражения Y есть число;
б) X - число, которое равно результату вычисления выражения Y.
Цель X is Y не имеет побочных эффектов и не может быть согласована
вновь. Если X не является неконкретизированной переменной или числом, или
если Y - не арифметическое выражение, возникает ошибка.
Примеры:
D is 10 -5
4 is 2 * 4 - 4
2 * 4 - 4 is 4
a is 3 + 3
X is 4 + а
2 is 4 - X
заканчивается успехом и D
становится равным 5
заканчивается успехом
заканчивается неудачей
заканчивается неудачей
заканчивается неудачей
заканчивается неудачей
Обратите внимание, что предикат is требует, чтобы его первый аргумент
был числом или неконкретизированной переменной. Поэтому М - 2 is 3
записано неверно. Предикат is не является встроенным решателем уравнений.
2.5 Сравнение результатов арифметических выражений
Системные предикаты =:=, -\-, , , - и - определены как инфиксные
операторы и применяются для сравнения результатов двух арифметических
15
выражений.
Для предиката @ доказательство целевого утверждения X@Y
заканчивается успехом, если результаты вычисления арифметических
выражений X и Y находятся в таком отношении друг к другу, которое задается
предикатом @.
Такое целевое утверждение не имеет побочных эффектов и не может
быть согласовано вновь. Если X или Y - не арифметические выражения,
возникает ошибка.
С помощью предикатов описываются следующие отношения:
X =:= Y
X=\=Y
X <Y
X >Y
X <= Y
X >= V
X равно Y
Хне равно Y
X меньше Y
X больше Y
X меньше или равно Y
X больше или равно Y
Использование предикатов иллюстрируют такие примеры:
а 5
5+2+7 5+2
3 + 2 =:= 5
3+2=5
2+1 =\= 1
N>3
заканчивается неудачей
заканчивается успехом
заканчивается успехом
заканчивается неудачей
заканчивается успехом
заканчивается успехом, если
N больше 3, и неудачей в противном
cлучае
3 Рекурсия
Рекурсия является мощным методом программирования. В Прологе она
приобретает особую важность, поскольку здесь отсутствуют циклические
конструкции while...do... и repeat...until, присущие обычным языкам
программирования. В данной главе на числовых примерах показаны идеи
рекурсии.
3.1 Стратегия «разделяй и властвуй»
Обычная стратегия решения задач состоит в том, чтобы разбить
исходную задачу на более мелкие подзадачи, решить их, а затем объединить
подзадачи с тем, чтобы получить решение исходной задачи. !? этом и
заключается стратегия «разделяй и властвуй». Может потребоваться разбиение
16
подзадачи на еще более мелкие и решение их но частям.
Если подзадача есть уменьшенный вариант исходной задачи, то t «особ ее
разбиения и решения идентичен примененному к исходной задаче.
Такой процесс называется рекурсией. Для того чтобы описанный метод
решения был результативным, он должен в конце концов принести к задаче,
решаемой непосредственно. Решить ее позволяют утверждения, называемые
граничными условиями.
Пример 3.1.1
Рассмотрим способы определения n-го терма в последовательности:
1,1,2,6,24,120,720,...
Первый вариант описания таков: нулевой терм равен 1, первый герм
получается при умножении 1*1, второй терм - при умножении 1 *1*2, третий при умножении 1*1*2*3, и в общем случае, n-й терм получается при
умножении 1*1*2*3*... *n.
С другой стороны, нулевой терм есть 1, а n-й терм есть (п - 1)-й терм,
умноженный на n. Данное определение является рекурсивным, поскольку
разбивает задачу нахождения n-го терма на задачи нахождения сначала (n-l)-ro
терма и умножения его затем на п.
Ясно, что оба определения эквивалентны, ибо, например, задача
вычисления третьего терма с помощью рекурсивного определения водится к
задаче вычисления второго терма и умножения его на 3. Задача вычисления
второго терма сводится к задаче вычисления нулевого терма и умножения его
на 1. Поскольку нулевой терм равен 1, вычислим первый терм - он будет равен
1. Это в свою очередь позволяет нам вычислить второй терм - он равняется 2, а
затем - третий терм, равный 6.
Для обозначения факта, что N-й член последовательности равен V,
воспользуемся предикатом посл(М,У). Рекурсивное определение выражается
следующими утверждениями Пролога:
/* ГРАНИЧНОЕ УСЛОВИЕ - нулевой терм равен 1 посл(0,1).
/* РЕКУРСИВНОЕ УСЛОВИЕ - если (N-D-й терм равен U,то
/* N-й терм равен U * N
посл(N,V) :M is N-1,
посл(М,U),
V is U * N.
Ответом на вопрос
17
?-посл(3,Z).
будет .
Z=6
другие решения (да/нет) ? нет
Заметим, что если на вопрос Пролог-системы мы ответим «да» вместо
«нет», то система попадает в бесконечный цикл. В гл. 8 мы покажем, как
избежать такой ситуации.
Для того чтобы представить, каким образом Пролог находит ответ,
рассмотрим вопрос
?-посл(3,Х).
и опишем работу Пролога на двух этапах: разбиения и решения задачи.
Фаза разбиения
Первое обращение. Пролог пытается удовлетворить запрос посл(3,Х),
используя первое утверждение процедуры описания последовательности
посл(0,1). Поскольку первая компонента терма (0) не сопоставляется с первой
компонентой запроса (3), попытка заканчивается неудачей.
Теперь Пролог пытается использовать второе утверждение, описывающее
последовательность. На этот раз голова второго утверждения посл(N,V)
сопоставляется с запросом посл(3,Х), при этом N получает значение 3, а V
связывается с X. Пролог переходит к доказательству целей в теле утверждения:
/*N=3
/*V=X
M is 3 - 1,
посл(М,U),
X is U * 3.
/ * откладывается
Первое целевое утверждение согласуется при М = 2. Второе целевое
утверждение посл(2,U) приводит ко второму рекурсивному обращению.
Пролог пытается согласовать третье целевое утверждение только при условии,
что согласовано второе. Поэтому третье целевое утверждение откладывается.
Второе обращение. Для того чтобы согласовать посл(2,U), Пролог
пытается сопоставить посл(2,U) с первым утверждением процедуры поcл (0,1),
но неудачно, поскольку первые компоненты термов не сопоставляются.
Однако удается сопоставить посл(2,U) с головой второго утверждения
посл(N2,V2) при N2, равной 2, и V2, равной U. Теперь Пролог пытается
18
согласовать тело утверждения:
/*N2=2
/*V2=U
M2 is 2 - 1,
посл(М2,U2),
U is U2 * 2.
/* откладывается
Мы использовали то же утверждение, что и в первом обращении, но с
меньшим аргументом N2.
Чтобы отличить второе обращение к утверждению от первого, присвоим
переменным индекс 2. В общем случае индекс n у переменной показывает, что
она применяется при n-м обращении. Переменные при первом обращении
записаны без индексов.
Первое из целевых утверждений согласуется при М2, равной 1. Второе
целевое утверждение приводит к третьему обращению, а третье целевое
утверждение откладывается.
Третье обращение. Чтобы согласовать посл(1,U2), Пролог пытается
сопоставить его с первым утверждением определения поcл(0,1), но неудачно.
Однако удается сопоставить посл(1,U2) с головой второго утверждения,
причем N3 получает значение 1, a V3 связывается с U2. Следовательно, теперь
надо согласовать цели:
/* N3=1
/*V3=U2
M3 is 1 - 1,
посл(M3,U3),
U2 is U3* 1.
/* откладывается
Первое целевое утверждение согласуется при МЗ, равной 0.
Второе целевое утверждение посл(0,U3) Пролог пытается согласовать,
сопоставляя его с первым утверждением определения. На этот раз два терма
успешно сопоставляются при U3, равной 1.
Редукция задачи завершена, и граничные условия позволили решить
последнюю задачу. Теперь Пролог возвращается к отложенным целевым
утверждениям и пытается согласовать самое последнее из них. Если удается это
сделать, Пролог переходит к согласованию предпоследнего отложенного
целевого утверждения и так далее, пока не будет согласовано первое. В данной
главе мы предполагаем, что все отложенные целевые утверждения успешно
согласуются. В гл.7 показано, что происходит, если Пролог не может
согласовать целевое утверждение.
19
Фаза решения задачи
Согласование посл(1,U2). Самое последнее из отложенных целевых
утверждений - третье целевое утверждение в третьем обращении: U2 is U3 * 1.
Поскольку переменная U3 равна 1 в результате согласования второго целевого
утверждения, U2 получает значение 1. Таким образом, посл(1,U2) согласуется
при U2, равной 1.
Согласование посл(2,U). Предпоследнее отложенное целевое утверждение - третье целевое утверждение во втором обращении: U is U2 * 2. Так
как переменная U2 равна 1 в результате согласования второго целевого
утверждения, U получает значение 2. Итак, посл(2,U) согласуется при U,
равной 2.
Согласование посл(3,Х). Предыдущее из отложенных целевых
утверждений - третье целевое утверждение в первом обращении: X is U * 3.
Поскольку переменная U равна 2 в результате согласования второго целевого
утверждения, X получает значение 6. Целевое утверждение посл(З,Х)
согласуется при X, равной 6.
Если отложенных целевых утверждений больше нет, Пролог определяет,
при каком значении переменной удовлетворяется запрос:
Х=6
другие решения (да/нет)? Нет
На рис.4.1.1 приведена полная трассировка запроса. Дуги, исходящие из
цели, помечены номером сопоставляемого утверждения и значениями
переменных при сопоставлении. Значения переменных, при которых
согласуется целевое утверждение, заключены в круглые скобки и записаны на
той же строчке, что и соответствующее целевое утверждение. Метки на дугах,
входящих в вершины со значениями переменных, являются целевыми
утверждениями, которые приводят к этим значениям.
20
Рис. 3.1.1. Трассировка запроса ?- посл(3,Х).
Пример 3.1.2
Требуется написать процедуру для вычисления ряда
f(x,n)=1+х+х^2+х^З+...+х^n
при известных действительном числе x и натуральном числе n.
Поскольку утверждения Пролога определяют некоторое отношение, а не
вычисляют значение, как функции Паскаля, нам нужно ввести третий параметр,
который обозначал бы сумму
1+x+x^2+x^3+. . .+x^n
для данных х и n. Тогда голова утверждения будет выглядеть следующим
образом: f(X,N,S). (Напомним, что переменные в Прологе должны начинаться с
заглавной буквы.)
Для того чтобы написать утверждения, мы должны преобразовать
приведенное выше определение ряда в рекурсивное определение.
Это можно сделать, учитывая, что
f(х,0) = 1
f(x,1) = 1 + x = f(x,0) * x + l
f(х,2) = 1 + х + х^2 = f(х,1) * x + 1
…
…
…
f(х,n) = 1 + х + х^2 + х^n = f(х,n-1) * х + 1
21
Определим ряд рекурсивно:
f(х,0) = 1
f(x,n) = f(x,n- 1) * х + 1
Такому определению соответствуют следующие утверждения Пролога:
/* ГРАНИЧНОЕ УСЛОВИЕ
f(x,01).
/* РЕКУРСИВНОЕ УСЛОВИЕ
f(x,n,s) :M is N - 1,
f(X,M,R),
S is R*X + 1.
На рис.4.1.2 показано, как Пролог согласует запрос ?- f(2,3,T).
Рис. 3.1.2. Трассировка запроса ?- f(2,3,T).
3.2 Восходящая стратегия
Мы рассмотрели процесс разбиения задач на подзадачи и их решения.
Так, в примере 4.1.1 для определения посл(4,V) нужно вычислить
посл(3,V2),посл(2,VЗ),...,посл(0,V5)
и построить решение, выбрав в качестве базиса граничное условие.
22
Рис.3.1.2. Трассировка запроса ?- f(2,3,T).
Другой подход заключается в том, чтобы, начав с граничного условия,
строить решение до тех пор, пока не будет решена исходная задача. Следуя
данному подходу и пытаясь найти четвертый член в последовательности, мы
должны начать с граничного условия 1, вычислить значение первого терма 1*1,
второго - 1*1*2, третьего -1*1*2*3 и, наконец, четвертого - 1*1*2*3*4. Такую
стратегию мы называем восходящей.
В описанном методе решения целевое утверждение должно иметь два
дополнительных параметра: один - для указания на «размер» решенной к
настоящему времени задачи, а второй - для записи промежуточного решения.
При вызове утверждения параметры связываются с граничным условием.
Пример 3.2.1
Если применить восходящую стратегию к решению задачи, взятой из
примера 4.1.1, мы получим следующие утверждения Пролога:
/* пoстp_пocл(NS,VS,N,V) строит N-й
/* терм последовательности -V
/* VS - это значение NS-гo терма
/* в построенной к данному моменту
/* последовательности
/* Требуемое решение:
постр_посл(N,V,N, V).
/* Построение следующего решения
постр_посл(NS,VS,N,V) :NSl is NS+1,
VSl is VS*NS1,
постр_посл (NS1, VS1 ,N, V).
23
Запрос включает граничное условие как решенную к настоящему времени
задачу. На запрос
?- постр_посл(0,1,7,V).
получаем
V=5040
другие решения (да/нет)? нет
Альтернативным вариантом является определение еще одного
утверждения, которое маскирует дополнительные параметры в постр_посл:
посл_п(N,V) :-постр_посл(0,1,N,V).
так что
?-nocл_п(7,V).
дает
V=5040
3.3 Рекурсия и эффективность
Определение рекурсии можно непосредственно выразить утверждениями
Пролога.
Однако следующий пример показывает, что для создания более
эффективной программы иногда лучше воспользоваться особенностями задачи.
Пример 3.3.1
Рассмотрим методы вычисления Х^n с помощью одного только
умножения. Здесь n - натуральное число (натуральным числом является
положительное целое число или нуль).
Очевидный способ - умножить X на себя n раз. Получим следующие
утверждения:
степень(Х,0,1).
степень(Х,N,R) :-
/*Х^0 равно 1
M is N - 1,
степень(X,M,Q),
R is Q * X.
24
Тогда ответом на запрос
?-степень(5,8,S).
будет:
S = 390625
Этот результат получен с помощью 8 умножений (рис.4.3.1).
Но можно также воспользоваться свойствами:
Х^n = Z * Z, где Z – Х^(n div 2), если n - четное, и
Х^n = Х^(n -1) * X, если n - нечетное,
и получить следующие утверждения:
степень_быстро(Х,0,1).
степень_быстро(Х,N,R) :О is N mod 2,
М is N div 2,
степень_быстро(Х,М,Q),
R is Q * Q.
степень_быстро(Х,N,R) :M is N - 1,
степень_быстро(Х,М,Q),
R is Q * X.
Во втором случае понадобится только 4 умножения (рис.4.3.2) при
обработке запроса
?- степень_быстро(5,8,S).
и будет получен ответ
S - 390625
Заметим, что в третьем утверждении степень_быстро нам не нужно было
проверять, является ли N нечетным. Мы предполагаем, что второе утверждение
не сопоставляется с целью в том случае, если N mod 2 не равно 0, т.е. N не
является четным. Следовательно, третье утверждение выбирается только тогда,
когда N является нечетным (за исключением вопросов об альтернативных
вариантах).
25
Рис. 4.31. Трассировка запроса ?-степень(5,8,S).
26
Рис. 3.3.2. Трассировка запроса степень_быстро(5,8,S)
Преимущество, которое имеет утверждение степень_быстро перед
утверждением степень, увеличивается с ростом N. Так, согласование
утверждения степень(2,1024,Х) требует 1024 умножений, в то нремя как
согласование утверждения степень_быстро(2,1024,Х) требует всего 11
умножений. Необходимость дополнительной проверки четности N и деления N
пополам приводит к тому, что практически только при больших N процедура
степень_быстро предпочтительнее, чем процедура степень.
Пример 3.3.2
Ряд Фибоначчи
0,1,1,2,3,5,8,13,21,34,55,89,144,...
определяется условиями:
f0 = 0
f1 = 1
fn = f(n-l)+f(n-2)
Мы можем непосредственно использовать определение ряда:
фиб(0,0).
фиб(1,1).
фиб(N,Х) :27
N1 is N - 1,
N2 is N - 2,
фиб(N1,X1),
фиб(N2,Х2),
X is X1 + X2.
Для того чтобы согласовать фиб(N,Х), потребуется 2*Y-1 рекурсивных
обращений. Здесь N больше нуля, a Y является (N+1)-м числом Фибоначчи.
Так, например, для согласования запроса
?-фиб(10,Х).
при
Х=55
потребуется 177 (89*2-1) обращений.
Однако можно переформулировать задачу следующим образом: найти Ne и (N+l)-e числа Фибоначчи. Тогда мы получим:
/* Нулевое число Фибоначчи равно 0, а
/* предшествующее число не определено
фиб(0,_,0).
/* Первое число Фибоначчи равно 1, а
/* предшествующее равно 0
фиб(1,0,1).
/* N-e число Фибоначчи образуется при
/* сложении двух предшествующих чисел
/* Фибоначчи
фиб(N,Р1,Р2) :M is N- 1,
фиб(М,F0,F1),
F2 is F0 + F1.
Чтобы найти 10-е число Фибоначчи, сделаем запрос:
?- фиб(10,F1,F2).
получим
F1=34
F2=55
другие решения (да/нет)? Нет
28
Для ответа на запрос было произведено 10 обращений. В общем для
нахождения N-го числа Фибоначчи (для N>0) требуется N обращений, причем с
ростом N увеличивается преимущество второго варианта процедуры перед
первым.
4 Структура данных
Термы Пролога позволяют выразить самую разнообразную информацию.
В настоящей главе мы рассмотрим два вида широко используемых структур
данных: списки и бинарные деревья, и покажем, как они представляются
термами Пролога.
4.1 Списки
Списковая форма записи
Задачи, связанные с обработкой списков, на практике встречаются очень
часто. Скажем, нам понадобилось составить список студентов, находящихся в
аудитории. С помощью Пролога мы можем определить список как
последовательность термов, заключенных в скобки.
Приведем примеры правильно построенных списков Пролога:
[джек, джон,фред,джилл,джон]
[имя(джон,смит),возраст(джек,24),Х]
[Х,Y,дата (12,январь, 1986) ,Х]
[]
Запись [Н | Т] определяет список, полученный добавлением Н в начало
списка Т. Говорят, что Н - голова, а Т - хвост списка [H | T].
На вопрос
?- L = [a | [b,c,d]].
будет получен ответ
L = [a,b,c,d]
а на запрос
?-L = [a,b,c,d], L2 = [2 | L].
- ответ
L=[a,b,c,d]
29
L2= [2,a,b,c,d]
Запись [Н | Т] используется для того, чтобы определить голову и
хвост списка.
Так, запрос
?- [X | Y] = [a,b,c].
дает
Х=а
Y=[b,c]
Заметим , что употребление имен переменных Н и Т необязательно.
Кроме записи вида [H | T], для выборки термов используются
переменные.
Запрос
?- [a,X,Y]=[a,b,c].
определит значения
х=b
Y=c
а запрос
?- [личность(Х) | Т]-[личность(джон),a,b].
- значения
Х=джон
Т=[а,b]
Некоторые стандартные целевые утверждения для обработки
списков
Покажем на примерах, как можно использовать запись вида [Н | Т]
вместе с рекурсией для определения некоторых полезных целевых утверждений
для работы со списками.
Принадлежность списку. Сформулируем
надлежности данного терма списку.
30
задачу
проверки
при-
Граничное условие:
Терм R содержится в списке [H | T], если R=H.
Рекурсивное условие:
Терм R содержится в списке [H | T], если R содержится в списке Т.
Первый вариант записи определения на Прологе имеет вид:
содержится(R,L) :L=[H | T],
H=R.
содержится(R,L) :L=[Н | Т],
содержится (R,Т).
Цель L=[H | Т] в теле обоих утверждений служит для того, чтобы
разделить список L на голову и хвост.
Можно улучшить программу, если учесть тот факт, что Пролог сначала
сопоставляет с целью голову утверждения, а затем пытается согласовать его
тело. Новая процедура, которую мы назовем принадлежит, определяется таким
образом:
принадлежит(R, [R | Т]).
принадлежит(R, [Н | Т]):-принадлежит(R,T).
На запрос
?- принадлежит(а,[а,b,с]).
будет получен ответ
да
на запрос
?- принадлежит(b, [а,b,с]).
-ответ
да
31
но на запрос
?- принадлежит(d,[а,b,с]).
Пролог дает ответ
нет
В большинстве реализаций Пролога предикат принадлежит является
встроенным.
Соединение двух списков. Задача присоединения списка Q к списку Р, в
результате чего получается список R, формулируется следующим образом:
Граничное условие:
Присоединение списка Q к [] дает Q.
Рекурсивное условие:
Присоединение списка Q к концу списка Р выполняется так: Q
присоединяется к хвосту Р, а затем спереди добавляется голова Р.
Определение можно непосредственно написать на Прологе:
соединить([],Q,Q).
соединить(Р,Q,R) :Р=[НР | ТР],
соединить (TP,Q,TR),
R=[HP | TR].
Однако, как и в предыдущем примере, воспользуемся тем, что Пролог
сопоставляет с целью голову утверждения, прежде чем пытаться согласовать
тело:
присоединить( [] ,Q,Q).
присоединить([HP | TP] ,Q, [HP | TR]) :присоединить (TP,Q,TR).
На запрос
?- присоединить([a,b,c], [d,e] ,L).
будет получен ответ
32
L=[a,b,c,d].
но на запрос
?- присоединить([a,b], [c,d], [e,f]).
ответом будет
нет
Часто процедура присоединить используется для получения списков,
находящихся слева и справа от данного элемента:
присоединить(L, [джим,R], [джек,билл,джим,тим,джим,боб]).
L= [джек,билл]
R=[тим,джим,боб]
другие решения (да/нет) ? да
L= [джек,билл,джим,тим]
R=[6o6]
другие решения (да/нет) ? да
других решений нет
Индексирование списка. Задача получения N-гo терма в списке
определяется следующим образом:
Граничное условие:
Первый терм в списке [Н | Т] есть Н.
Рекурсивное условие:
N-й терм в списке [Н | Т] является (N-l)-м термом в списке Т.
Данному определению соответствует программа:
/* Граничное условие:
получить ([H | Т], 1,Н).
/* Рекурсивное условие:
получить ([H | Т] ,N,Y) :M is N - 1,
получить (T,M,Y).
33
Построение списков из фактов. Иногда бывает полезно представить в
виде списка информацию, содержащуюся в известных фактах. В большинстве
реализаций Пролога есть необходимые для этого предикаты:
bagof(X,Y,L)
определяет список термов L, конкретизирующих
переменную X, как аргумент предиката Y, которые
делают истинным предикат Y
setof(X,Y,L)
все сказанное о предикате bagof относится и к setof,
за исключением того, что список L отсортирован и из
него удалены все повторения.
Если имеются факты:
собака(рекc).
собака (голди).
собака (фидо).
собака(рекc).
то на запрос
?- bagof (D,собака(D),L).
будет получен ответ
L=[рекс,голди,фидо,рекс]
в то время как
?- setof (D .собака (D),L).
дает значение
L- [фидо,голди,рекc]
Пример: сложение многочленов
Теперь мы достаточно подготовлены к тому, чтобы использовать списки
для решения задач. Вопрос, которым мы займемся, - представление и сложение
многочленов.
Представление многочленов. Посмотрим, как можно представить
34
многочлен вида
Р(х)=3+Зх-4х^З+2х^9
Q(х)=4х+х^2-Зх^З+7х^4+8х^5
Заметим, что каждое подвыражение (такое, как Зх ^3, Зх, 3) имеет самое
большее две переменные компоненты: число, стоящее перед х, называемое
коэффициентом, и число, стоящее после ^ - степень. Следовательно,
подвыражение представляется термом
х(Коэффициент.Степень)
Так, 5х^2 записывается как х(5,2), х^З представляется как х(1,3), а
поскольку х^0 равно 1, подвыражению 5 соответствует терм х(5,0).
Теперь запишем многочлен в виде списка. Приведенный выше многочлен
Р(х), например, будет выглядеть следующим образом:
[x(3,0),’+’,x(3,1),’-’,x(4,3),’+’,x(2,9)]
Воспользуемся тем, что многочлен
3+Зх-4х^З+2х^9
допускает замену на эквивалентный
3+Зх+(-4)х^3+2х^9
Тогда он выражается списком:
[x(3,0),’+’,x,(3,1),’+’,x(-4,3),’+’,x(2,9)]
В такой записи между термами всегда стоят знаки '+'. Следовательно, их можно
опустить, и многочлен принимает окончательный вид:
[х(3,0),х(3,1),,х(-4,3),х(2,9)]
Подразумевается, что между всеми термами списка стоят знаки '+'.
Представлением многочлена Q (х) будет
[х(4,1),х(1,2),х(-3,3),х(7,4),х(8,5)
35
Сложение многочленов. Теперь напишем целевые утверждения для
сложения двух многочленов. Сложение многочленов
3-2x^2+4x^3+6x^6
-1+Зх^2-4х^З
в результате дает
2+х^2+6х^6
Аргументами
целевого
утверждения
являются
многочлены,
представленные в виде списков. Ответ будет получен также в виде списка.
Сложение многочлена Р с многочленом Q осуществляется следующим
образом: .
Граничное условие:
Р, складываемый с [], дает Р.
[] , складываемый с Q, дает Q.
Рекурсивное условие:
При сложении Р с Q, в результате чего получается многочлен R,
возможны 4 случая:
а) степень первого терма в Р меньше, чем степень первого терма в Q. В
этом случае первый терм многочлена Р образует первый терм в R, а хвост R
получается при прибавлении хвоста Р к Q. Например, если Р и Q имеют вид
Р(х)=Зх^2+5х^3
Q(x)=4x^3+3x^4
то первый терм R(x) равен Зх^2 (первому терму в Р(x)). Хвост R(x) равен
9х^З+Зх^4, т.е. результату сложения Q(x) и хвоста Р(х) ;
б) степень первого терма в Р больше степени первого терма в Q. В
данном случае первый терм в Q образует первый терм в R, а хвост R
получается при прибавлении Р к хвосту Q. Например, если
Р(х)=2х^3+5х^4
д(х)=Зх^3-х^4
то первый терм R(x) равен Зх^2 (первому терму в Q(x)), а хвост R(x)
36
равен 2х^З+4х^4 (результату сложения Р(х) и хвоста Q(x));
в) степени первых термов в Р и Q равны, а сумма их коэффициентов
отлична от нуля. В таком случае первый терм в R имеет коэффициент, равный
сумме коэффициентов первых термов в Р и Q. Степень первого терма в R равна
степени первого терма в Р (или Q). Хвост R получается при сложении хвоста Р
и хвоста Q. Например, если Р и Q имеют вид
Р(х)=2х+Зх^3
Q(x)=3x+4x^4
то первый терм многочлена R(x) равен 5х (результату сложения первого терма
в Р(х) с первым термом в Q(x)). Хвост R(x) равен 3x^3+4x^4 (результату
сложения хвоста Р(х) и хвоста Q(x));
г) степени первых термов в Р и Q одинаковы, но сумма коэффициентов
равна нулю. В данном случае многочлен R paвен результату сложения хвоста Р
с хвостом Q. Например, если
Р(х)=-2+2х
Q(x)=2-3x^2
то
К(х)=2х-Зх^2
(это результат сложения хвостов многочленов Р(х) и Q(x)).
Рассмотренный процесс сложения многочленов можно непосредственно
записать на языке Пролог:
/* Граничные условия
слож_мн([] ,Q,Q).
слож_мн (Р,[] ,P).
/* Рекурсивное условие
/*(а)
слож_мн([х(Рс,Рр) | Pt],[x(Qc,Qp) | Qt],
[x(Pc,Pp) | Rt]):PpQp,
слож_мн(Рt, [x(Qc,Qp) | Qt] ,Rt).
/*(6)
слож_мн([х(Рс,Рр) | Pt],[x(Qc,Qp) | Qt],
37
[x(Qc,Qp) | Rt]):PpQp,
слож_мн( [x(Pc,Pp) | Pt] ,Qt,Rt) .
/* (в)
слож_мн ( [x (Pc.Pp) | Pt] , [x (Qc.Pp) | Qt] ,
[x(Rc,Pp) | Rt]):Rc is Pc+Qc,
Rc\= 0,
слож_мн(Рt,Q1,Rt) .
/*(г)
слож_мн( [x(Pc,Pp) | Pt] , [x(Qc,Pp) | Qt] ,Rt) :Rc is Pc+Qc,
Rc= 0,
слож_мн(P1,Q1,R1)
Заметим, что в двух последних утверждениях проверка на равенство
осуществляется следующим образом: степени первых термов складываемых
утверждений обозначает одна и та же переменная Pp.
Списки как термы. В начале главы мы упомянули о том, что список
представляется с помощью терма. Такой терм имеет функтор '.', два аргумента
и определяется рекурсивно. Первый аргумент является головой списка, а
второй - термом, обозначающим хвост списка. Пустой список обозначается [].
Тогда список [а,b] эквивалентен терму .(а,.(b,[])).
Таким образом, из списков, как и из термов, можно создавать вложенные
структуры. Поэтому выражение
[[a,b],[c,d],[a],a]
есть правильно записанный список, и на запрос
?- [H | T]=[[a,b],c]
Пролог дает ответ
H=[а,b]
T =[с]
4.2.Бинарные деревья
Представление бинарных деревьев
Бинарное дерево определяется рекурсивно как имеющее левое поддерево,
корень и правое поддерево. Левое и правое поддеревья сами являются
38
бинарными деревьями. На рис.5.2.1 показан пример бинарного дерева.
d
e
Рис.4.2.1. Бинарное дерево
Такие деревья можно представить термами вида
бд(Лд,К,Пд),
где Лд - левое поддерево, К - корень, а Пд - правое поддерево. Для обозначения
пустого бинарного дерева будем использовать атом nil.
Бинарное дерево на рис.5.2.1 имеет левое поддерево
бд(бд(nil,d,nil),b,бд(nil,e,nil))
правое поддерево
бд(nil,c,nil)
и записывается целиком как
бд(бд(nil,d,nil),b,бд(nil,e,nil))
а,
бд(nil,c,nil)
Представление множеств с помощью бинарных деревьев
Описание множеств в виде списков позволяет использовать для множеств
целевое утверждение принадлежит, определенное ранее для списков.
Однако для множеств, состоящих из большого числа элементов,
списковые целевые утверждения становятся неэффективными. Рассмотрим,
например, как целевое утверждение принадлежит (см. разд. 5.1.2) позволяет
моделировать принадлежность множеству. Пусть L - список, описывающий
множество из первых 1024 натуральных чисел. Тогда при ответе на запрос
?- принадлежит(ЗООО,L).
Прологу придется проверить все 1024 числа, прежде чем заключить, что такого
числа нет:
нет
39
Представление множества бинарным деревом позволяет добиться
лучшего результата. При этом бинарное дерево должно быть упорядочено
таким образом, чтобы любой элемент в левом поддереве был меньше, чем
значение корня, а любой элемент в правом поддереве -больше. Поскольку мы
определили поддерево как бинарное дерево, такое упорядочение применяется
по всем поддеревьям. На рис.5.2.2 приведен пример упорядоченного бинарного
дерева.
Дерево на рис.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,
принадлежит_дереву (Х,Пд).
40
/* 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)
41
включ_бд(бд(Лд,К,Пд),Х,бд(Лднов,К,Пд)) :Х@К,
включ_бд(Лд,Х,Лднов).
/*(2)
включ_бд(бд(Лд,К,Пд),Х,бд(Лд,К,Пднов)):Х@К,
включ_бд(Пд,Х,Пднов).
На запрос
?- включ_бд(nil,d,T1) ,включ_бд (Т1 ,а,Т2).
будут получены значения
T1=бд(nil,d,nil)
T2=бд(бд(nil,a,nil),d,nil)
Процедуру включ_бд() можно использовать для построения упорядоченного дерева из списка:
/* Граничное условие:
список_в_дерево([],nil).
/* Рекурсивное условие:
список_в_дерево([Н | Т],Бд) :список_в_дерево(Т,Бд2),
включ_бд(Н,Бд2,Бд).
Заметим, что включ_бд не обеспечивает построения сбалансированного
дерева. Однако существуют алгоритмы, гарантирующие такое построение.
Создание отсортированного списка. Для построения отсортированного
списка элементов воспользуемся упорядоченным бинарным деревом.
Граничное условие:
Пустое бинарное дерево (nil) приводит к пустому списку [].
Рекурсивное условие:
Отсортированный список для упорядоченного бинарного дерева
бд(Лд,К,Пд), где Лд имеет отсортированный список СЛ, а Пд отсортированный список СП, получается присоединением [К | СП] к СЛ.
Рассмотрим, например, упорядоченное бинарное дерево
42
бд(бд(бд(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) ассоциативность.
5.1 Операторы и структуры
Синтаксис операторов
Структура в Прологе образуется из атома, называемого главным
43
функтором, и следующей за ним последовательности термов, называемых
компонентами. Последовательность компонент заключается в круглые скобки.
Между главным функтором и открывающей скобкой не должно быть пробела.
Компоненту разделяются запятыми. Например, +(1,2) представляет собой
структуру с главным функтором + и компонентами 1 и 2.
Для некоторых типов структур допустима более удобная запись с
помощью альтернативных форм синтаксиса:
а) синтаксис операторов для структур, арность которых равна одному или
двум;
б) синтаксис списков для структур в виде списков;
в) синтаксис строк для списков символов, записанных в кодах ASCII или
EBCDIC.
В настоящей главе мы обсудим первую форму - синтаксис операторов.
Структуры с арностью, равной одному или двум, могут быть переписаны в
синтаксисе операторов посредством объявления главного функтора структуры
оператором с одним или двумя аргументами. Компоненты структуры
записываются как аргументы оператора.
Например, если + и - объявить системными операторами, то +(1,2)
принимает вид 1+2, а-(1) записывается как -1.
Если арность структуры равна единице, оператор может быть объявлен
как
а) префиксный оператор. В этом случае оператор записывается перед
единственным аргументом ор А;
б) постфиксный оператор. В этом случае оператор записывается после
единственного аргумента А ор.
Если арность структуры равна двум, оператор может быть объявлен как
инфиксный. В таком случае он записывается между двумя своими
аргументами: А ор В.
Например, такие структуры, как
+(X,Y).
;(P,Q).
(; обозначает 'или')
< (X,Y).
является_частью(А,В)
прошло(Р).
допускают общепринятую форму записи
X+Y.
P;Q.
X<Y.
А является_частью В.
44
Р прошло.
Максимальное число компонент в структуре операторного типа равно
двум. Следует подчеркнуть, что синтаксис операторов используется вместо
обычного формата структур только для удобства.
Свойства операторов
Для точного определения оператора необходимо указать три его
свойства: позицию, приоритет и ассоциативность.
Позиция оператора указывает, где он записывается по отношению к
своим аргументам.
Приоритет оператора задает порядок выполнения операций в
выражении, содержащем более одного оператора.
Ассоциативность оператора показывает, какая операция выполняется
первой в выражении, содержащем два или более оператора с одинаковым
приоритетом.
Теперь мы поясним значение свойств и их использование в объявлении
оператора.
5.2 Позиция операторов
Позиция оператора в выражении определяет его тип. Как было сказано
выше, оператор может относиться к одному из трех типов.
Инфиксный оператор располагается между двумя аргументами,
например: X + Y или X < Y .
Префиксный - оператор располагается перед единственным
аргументом, например: not X или - X.
Постфиксный - оператор располагается после единственного аргумента,
например: X факториал или Р прошло
5.3 Приоритет операторов
Каждый оператор имеет приоритетный номер, указываемый при
объявлении оператора. Номер является целым числом и обычно '"' находится в
пределах от 1 до 1500.
Если выражение содержит более одного оператора, например
X + Y*2,
то возникает неопределенность в порядке выполнения операций. Эта проблема
разрешается посредством назначения всем операторам приоритетного номера.
45
Вычисления осуществляются, начиная с оператора, имеющего наименьший
номер, и заканчивая оператором с наибольшим номером. В нашем примере X +
Y * 2 оператор * имеет более низкий приоритетный номер, чем оператор +.
Поэтому, как и следовало ожидать, выражение вычисляется так: X + (Y * 2).
Теоретический приоритетный номер скобок меньше нуля. Выражение,
заключенное в скобки, рассматривается как один терм. Скобки могут
использоваться для того, чтобы изменить правила установления
ассоциативности и сделать синтаксис выражений более четким.
5.4 Ассоциативность операторов
Ассоциативность оператора указывает, как следует интерпретировать
выражение, содержащее несколько операторов с одинаковым приоритетом.
Покажем на примере необходимость такой информации.
Рассмотрим выражение a/b/с. Результат его вычисления зависит от
расстановки скобок:
(а/b)/с
или
а/(b/с)
Арифметические операции +, -(минус), * и / определены в Прологе как
левоассоциативные. Это означает, что они должны иметь слева операторы
равного или низшего приоритета, а справа - операторы строго низшего
приоритета.
Следовательно, приведенное выше выражение в соответствии с правилом
можно вычислить только как (а/b)/с, а не как а/(b/с).
5.5 Спецификаторы
Позицию и ассоциативность оператора удобно задавать с помощью
спецификатора. Перечислим виды спецификаторов:
fx
fy
xf
yf
xfx
xfy
yfx
yfy
для определения постфиксного оператора;
для определения префиксного оператора;
для определения инфиксного оператора;
" (правоассоциативногооператора);
" (левоассоциативного оператора);
В такой форме записи приняты следующие соглашения:
f представляет оператор. Им может быть системный оператор или
46
оператор, объявленный с помощью предиката ор;
х представляет выражение, содержащее операторы только низшего
приоритета по отношению к приоритету f;
у представляет выражение, которое может содержать операторы равного
или низшего приоритета по отношению к приоритету f.
Если оператор объявлен со спецификатором yfx, то он является
левоассоциативным и подвыражение у вычисляется первым. Возьмем в
качестве примера
5 + 4*10/5.
Здесь операторы * и / имеют одинаковый приоритет, но, поскольку они
определяются в Прологе как левоассоциатииныс, выражение будет вычисляться
следующим образом:
У
(5+4*10)
(5+(4*10))
f
/
/
x
5
5
Следовательно, (5+(4*10))/5 равно 9, если использовать правило
определения приоритета.
Если оператор объявлен со спецификатором xfy, то он является
правоассоциативным и подвыражение у, стоящее справа, вычисляется первым.
Спецификатор yfy недопустим в Прологе, так как выражения,
содержащие операторы с такой ассоциативностью, окажутся неопределенными.
Если бы * и / были объявлены со спецификаторами yfy, тогда выражение из
предыдущего примера 5+4*10/5 можно было бы вычислить либо как прежде
(5+(4*10))/5=9,
либо как
5+(4*(10/5))=13
(суммирование + объявлено с большим приоритетом, чем * или /).
Применение спецификатора xfx в Прологе допустимо. Он используется,
например, при объявлении операторов сравнения, таких, как < и >. Обычно
трудностей не возникает, так как для операторов < и > в Прологе определяется
более высокий приоритет, чем для арифметических операторов. Следовательно,
правая и левая части выражения должны быть вычислены до того, как будет
47
произведена попытка выполнить функцию сравнения, например,
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)*>с по умолчанию.
Некоторые реализации Пролога в таких неясных случаях выдают
сообщение об ошибке.
5.6 Операторы объявления
Пролог предоставляет пользователю возможность определять свои
собственные операторы. Для этого служит встроенный оператор ор. Функтор
ор имеет три аргумента: приоритет, спецификатор и имя функтора:
op(P,S,N)
Здесь Р - приоритет оператора, S - спецификатор, а N - имя атома,
который будет использоваться в качестве главного функтора в
48
соответствующей структуре.
Пролог имеет ряд встроенных операторов, которые перечислены в конце
главы. Все они определены так, как если бы для каждого объявления было
успешно выполнено целевое утверждение
?-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).
5.7 Системные операторы
Обычно в реализации языка Пролог системные операторы,
позиция/ассоциативность и приоритет объявляются следующим образом:
Имя
:?;
,xfy
spy
nospy
Спецификатор
xfx
fx
xfy
1000
fx
fx
49
Приоритет
1200
1200
1100
900
900
их
not
.
=..
=
\=
is
=:=
=\=
<
=<
>
>=
==
\==
—
+
/
*
div
mod
fx
xfy
xfx
xfx
xfx
xfx
xfx
xfx
xfx
xfx
xfx
xfx
xfx
xfx
yfx
yfx
yfx
yfx
yfx
xfx
800
750
700
700
700
700
700
700
700
700
700
700
700
700
500
500
400
400
400
300
6 Встроенные предикаты
В настоящей главе рассматриваются встроенные предикаты Пролога,
описываются способы обновления базы данных, вопросы ввода/вывода и
обработки файлов.
6.1 Встроенные предикаты
После загрузки системы Пролог пользователю становится доступен ряд
системных предикатов. Они определяются с помощью либо самого Пролога,
либо языка низшего уровня, используемого для реализации Пролога.
Описание предикатов и способы их применения содержатся в документации по конкретной реализации Пролога. При доказательстве некоторых
предикатов имеет место так называемый побочный эффект. Благодаря ему
обеспечивается интерфейс с пользователем и обработка файлов. Предикаты,
обладающие побочным эффектом. обычно реализуются не на Прологе, а на
языке низкого уровня, например Ассемблере, либо на языке высокого уровня,
таком, как Паскаль.
Встроенные предикаты не могут быть изменены пользователем, а
попытка заменить их, применяя предикаты assert и retract, приведет к
сообщению об ошибке.
6.2 Обновление базы данных Пролога
Программная среда Пролога состоит из двух частей:
программный модуль Пролога;
50
база данных Пролога.
Программный модуль включает интерпретатор Пролога (в некоторых
реализациях и компилятор), драйвер ввода/вывода и, кроме того, системные
процедуры. Пользователь обычно не имеет возможности изменять
программный модуль.
База данных Пролога содержит набор основных, заранее определенных
предикатов. Кроме того, резервируется пространство для добавления
предикатов, которые будут объявлены пользователем. Механизмы добавления,
удаления
и
обновления
утверждений,
заданных
пользователем,
классифицируются следующим образом:
1) добавление и удаление утверждений;
2) добавление утверждений из файла и замена имеющихся утверждений
на утверждения, находящиеся в файлах для того же самого предиката (ввод
программ и повторный ввод программ).
Файл Пролога состоит из утверждений. Утверждение есть факт или
правило. Утверждения обрабатываются интерпретатором (компилятором)
Пролога и потому должны удовлетворять синтаксическим определениям.
Добавление и удаление утверждений
Как уже говорилось, в Прологе имеется набор встроенных предикатов,
позволяющих добавлять или удалять утверждения из базы данных. В гл.7 мы
ввели предикаты asserta, assertz и retract, служащие для обновления базы
данных. Подытожим ранее сказанное и рассмотрим встроенные предикаты
retractall и abolish.
assertz(X)
X должен быть конкретизирован термом, не являющимся переменной.
Терм интерпретируется как утверждение и добавляется в базу данных Пролога.
Утверждение помещается в конец базы данных, или если существует процедура
с теми же функтором и арностью, то добавляется к этой процедуре. (В
некоторых реализациях Пролога допустимы исключения из приведенного
правила, потому необходимо обратиться к руководству по конкретной
системе.)
asserta(X)
Определяется аналогично предикату assertz(X), но новое утверждение
становится первым утверждением в процедуре с теми же функтором и
арностью.
retract (X)
51
X должен быть конкретизирован составным термом. В базе данных
осуществляется поиск утверждения, голова и тело которого сопоставляются с
термом X. Первое такое утверждение затем удаляется.
retractall(X)
Удаляет из базы данных все утверждения, функтор и арность которых
сопоставимы с X.
abolish(P,A)
Удаляет из базы данных все утверждения для процедуры Р с арностью А.
Описанные предикаты служат для обновления базы данных Пролога во
время выполнения программы.
Возможность динамического обновления программы довольно часто
применяется в Прологе. После удаления части базы данных с помощью
предикатов retract и abolish необходимо запустить механизм «сборки мусора»
для восстановления памяти. Алгоритм, выбранный для чистки памяти, зависит
от реализации Пролога.
Считывание утверждений в базу данных
Для того чтобы добавить новые утверждения в базу данных, пользователь
вводит
:- consult(user).
Такая команда Пролог-системы вызывает считывание утверждений с
терминала пользователя до тех пор, пока не встретится символ конец_файла.
Типичная последовательность имеет вид:
?- consult(user).
: утверждение 1
: утверждение 2
:$
?-
/* начало считывания
/* утверждений
/* Обратите внимание, что
/* использован другой знак
/* приглашения системы
/* символ конец_файла
/* снова появилось обычное
/* приглашение системы
Утверждения 1 и 2 добавлены к базе данных и будут выбраны при
попытке Пролога доказать цель с теми же функтором и арностью.
Более короткий вариант записи consult(user):
52
?- [user].
В данном случае используется тот же символ конца файла $.
Пользователю будет нелегко вводить одни и те же предикаты при каждом
новом сеансе работы. Для устранения нежелательной ситуации в Прологе
реализован механизм, позволяющий считывать группы предикатов или
программы пользователя. Пусть мой-файл - файл пользователя. Утверждение
?- consult(мой-файл).
считывает содержимое файла мой-файл. Целевое утверждение consult(X)
всегда выполняется успешно. В результате утверждения из файла мой-файл
добавляются в базу данных Пролога. Если при чтении одного из утверждений
обнаруживается синтаксическая ошибка, выдается сообщение об этом
утверждении, но обработка оставшихся утверждений продолжается до тех пор,
пока не будет найден конец файла. Целевое утверждение consuIt(X) нельзя
передоказать. Если X не является именем реально существующего файла или
файл не может быть открыт, то выдается сообщение об ошибке. При вводе
программы из файла в ней иногда встречаются команды или директивы,
относящиеся к системе Пролог. Они отличаются от утверждений, добавляемых
в базу данных, тем, что им предшествует оператор ?- или:-.
Целевое утверждение consult(F) добавляет утверждения из файла F в
конец существующей базы данных. Пролог пытается сопоставить цель с
добавленным утверждением только в том случае, если при прямом
доказательстве
попытка
согласования
существующего
утверждения
закончилась неудачей. Допускается замена новым утверждением утверждения,
ранее записанного в базу данных, для чего используется встроенный предикат
reconsult. Он работает аналогично предикату consult, но утверждения,
считываемые в базу данных, замещают ранее записанные утверждения с теми
же функтором и арностью.
В качестве аргументов предикатов consult и reconsult могут выступать
списки файлов. Например:
consult([‘F1’,’F2’,...]).
reconsult([‘F1’,’F2’,...]).
Предикаты сохранения и восстановления
После добавления новых утверждений в базу данных пользователь имеет
возможность сохранить копию текущего состояния системы и в следующем
сеансе продолжить работу, начиная с записанного состояния. Для этого
53
применяется предикат save. Утверждение
save('мой-файл').
сохраняет текущее состояние базы данных Пролога в файл, называемый мойфайл. Целевое утверждение save(X) нельзя передоказать. В случае, если не
существует файла с именем X или если его невозможно открыть, то выдается
сообщение об ошибке.
Успешно выполнив сохранение текущего состояния базы данных Пролога
в файл, мы можем вернуться к прежнему состоянию, используя предикат
restore:
геstоrе('мой-файл').
6.3 Особенности ввода и вывода
Чтение символов
Системные предикаты get(X), get0(X), skip(X) служат для ввода символов
с терминала пользователя. Коды символов различаются в зависимости от
используемой реализации Пролога. В микрокомпьютерах в основном
применяется код ASCII*, а в больших ЭВМ (таких, как ICL2900) - код
EBDIC**.
Перечисленные предикаты определяются следующим образом.
get0(X)
Предикат вызывает считывание одного символа с клавиатуры. Если
переменная X неконкретизирована, то X принимает значение кода символа.
Если же переменная X конкретизирована, то вводимый символ сопоставляется
с X. Целевое утверждение будет успешно доказано только в том случае, если
символ сопоставим с X.
get(X)
Предикат вызывает считывание одного или нескольких символов с
клавиатуры до тех пор, пока не будет считан символ пробела. Если переменная
X неконкретизирована, то код символа отождествляется с X и доказательство
утверждения заканчивается успехом. Если же переменная X конкретизирована,
целевое утверждение будет доказано только в случае, если вводимый символ
сопоставим с X.
skip(X)
54
Предикат считывает символы с клавиатуры или из текущего входного
файла, пока не встретится символ с кодом X, тогда доказательство цели
заканчивается успешно. Переменная X может быть целочисленным
выражением.
Запись символов
Для вывода символов используются системные предикаты: put(X), tab(X)
nl. Они определяются следующим образом.
put(X)
Символ с кодом X записывается на терминал. Допускается, чтобы X был
целочисленным выражением. Доказательство целевого утверждения put(X)
всегда заканчивается успехом. X может быть кодом как управляющего, так и
алфавитно-цифрового символа.
tab(X)
На терминал выводится X пробелов. X может быть целочисленным
выражением.
nl
Происходит переход на новую строку терминала.
Рассмотрим примеры:
put(65)
put(66+32)
put('C')
put([100])
put(‘e’+’A’-‘a’)
put(a(b))
на терминал выводится символ А
(подразумевается ASCII код)
выходится символ b
выводится символ С
выводится символ d
выводится символ Е
выдается сообщение об ошибке
Считывание термов
Предикаты
read(X), readb(X) и read(X,Y)
служат для считывания термов. Приведем их определение.
read(X)
Если переменная неконкретизирована, то она означивается термом,
55
считанным с терминала. Попытка пользователя напечатать выражение, не
являющееся термом, приведет к ошибке. Вводимая информация должна
заканчиваться точкой. В случае, если переменная X конкретизирована термом,
попытка доказать целевое утверждение заканчивается успехом только при
условии, что вводимый с терминала терм сопоставим с X. В противном случае
попытка доказательства завершается неудачей.
readb(X)
Данный предикат аналогичен предикату read(X), но вводимый терм не
должен заканчиваться точкой.
read(X,Y)
Доказательство предиката завершается успехом, если считываемый терм
отождествляется с X, и неудачей в противном случае. При успешном
доказательстве переменная Y становится списком, состоящим из имен
переменных, входящих в терм X. Пролог переименовывает переменные,
встретившиеся в вводимом терме. Таким образом, когда попытка доказать
целевое утверждение read(X,Y) заканчивается успехом, список Y содержит
внутренние имена переменных, входящих в терм X. Как и при использовании
предиката read(X), после вводимого терма должна стоять точка.
readb(X,Y)
Предикат аналогичен предикату read(X,Y). Различие состоит в том, что
пользователь не должен печатать точку после ввода терма.
Примеры:
После запроса:
?-read(X).
система ожидает ввода. Если пользователь напечатает:
test (5).
то в результате переменная X получит значение
Х- test (5)
Задав вопрос
?- read (джек (мужчина)).
56
и введя терм
test.
получим ответ:
нет.
При запросе:
?-read(X,Y).
вводим терм
fred(P,a(Q)).
в результате получаем
X = fred(_55,a(_57))
Y=[[P |_55],[Q | _57]]
где _55 - внутреннее имя переменной Р, а _57 - внутреннее имя переменной Q.
Запись термов
Рассмотрим предикаты, служащие для записи термов.
write(X)
Данный предикат выводит терм на экран. Если X содержит конкретизированные переменные, то на экране отображаются термы, которыми эти
переменные конкретизированы. Если X содержит неконкретизированные
переменные, они выводятся как уникальные целочисленные переменные.
Сцепленные переменные, входящие в X, отображаются в виде одного и того же
целого значения. Например, в ответ на запрос
?- write(cyммa(N,S,N+l)).
на экране появится
сумма (_35,_36 is_35+l ) .
writeq(X)
57
Предикат аналогичен предикату write(X) кроме случая, когда X содержит
атом или функторы, которые в программе следует заключать в кавычки.
Предикат writeq записывает такие величины на экране в кавычках. Например, в
результате запроса
?- writeq('fred.txt').
на экране появится
'fred.txt'
display (X)
Предикат выводит терм X в стандартной скобочной префиксной
форме записи. Например, запрос
?- display (X+Y).
вызовет появление на экране записи:
+ (X,Y)
6.4. Обработка файлов
Описанные выше предикаты ввода/вывода осуществляют считывание и
запись на терминал. Клавиатура связывается со стандартным входным потоком,
а экран - со стандартным выходным потоком. Входной и выходной потоки
могут осуществлять связь с различными файлами пользователя, требующимися
во время сеанса работы с Пролог-системой. Для этого файл объявляется в
качестве стандартного входного потока, и рассмотренные выше предикаты для
ввода производят считывание из файла, а не с клавиатуры. Подобным образом
файл назначается в качестве стандартного выходного потока. В таком случае
приведенные предикаты для вывода осуществляют запись в файл, а не на экран.
Для переопределения стандартных потоков используются следующие
предикаты.
see(F)
Определяет в качестве стандартного входного потока файл F.
seen(F)
Предикат переопределяет стандартный входной поток с файла F на
клавиатуру. Прежде чем с помощью предиката see(F2) определить в качестве
58
стандартного входного потока файл F2, следует с помощью предиката seen(F1)
отменить связь файла F1 со стандартным входным потоком.
tell(F)
Целевое утверждение определяет стандартным выходным потоком файл
F.
told(F)
Целевое утверждение переопределяет стандартный выходной поток с
файла F на экран. Перед тем как с помощью предиката tell(F2) определить в
качестве выходного потока новый файл F2, необходимо с помощью предиката
told(F1) переключить текущий выходной поток с файла F1.
Помимо перечисленных в Прологе есть два предиката, позволяющие
выяснить, с какими файлами связаны стандартные потоки.
seeing(F)
F отождествляется с именем файла, связанного со стандартным входным
потоком.
telling(F)
F отождествляется с именем файла, связанного со стандартным
выходным потоком.
Если при выполнении описанных выше предикатов оказывается, что файл
F не существует или переменная F не конкретизирована, возникает ошибка.
Кроме стандартных входного и выходного потоков, в большинстве
реализаций Пролога используются и другие потоки, служащие для связи с
файлами.
open(F,S,A)
Предикат открывает файл F в поток S. Если значением А является read,
файл открывается для чтения, а если А принимает значение write, то файл
открывается для записи.
close(S)
Предикат закрывает поток S.
При выполнении рассматриваемых целевых утверждений должны быть
конкретизированы переменные: S - имя потока, F - имя файла, А - режим
доступа.
Если поток S открыт, мы должны использовать все имеющиеся
предикаты ввода и вывода, указывая имя потока в качестве первого аргумента.
Например, в результате выполнения последовательности утверждений
59
?- open(тестфайл,элемент,write),
write (элемент,тестпоток),
n1 (элемент) ,close (элемент).
в файл тестфайл будут выведены запись 'тестпоток' и признак новой
строки.
Редактирование программ на Прологе
В большинстве Пролог-систем есть встроенный редактор, позволяющий
пользователю вносить изменения в программу во время сеанса работы с
Прологом. Если такой возможности нет, необходимо выйти из среды Пролога,
отредактировать программу и снова вернуться в Пролог-систему. Обычно для
редактирования файла в Прологе используется предикат
edit(F),
где F - имя файла.
После того как файл отредактирован, он автоматически повторно
вводится в базу данных Пролога для поддержания ее в актуальном состоянии.
Распечатка предикатов
Для того чтобы проверить, какие утверждения входят в программу,
можно воспользоваться следующими предикатами :
listing
Предикат выводит все утверждения, содержащиеся в базе данных
Пролога, в стандартный выходной поток.
listing(C)
Если С - атом, то в стандартном выходном потоке печатаются все
процедуры с функтором С. Если С - терм, имеющий функтор F арности А, то
печатаются только процедуры с функтором F и арностью А.
В результате выполнения предиката
listing(фред/2)
будет напечатан список всех утверждений, головой которых является
функтор 'фред', а арность равна двум.
60
7 Отладка
В данной главе рассматриваются процесс отладки программ и . способ
получения пользователем информации о внутренних функциях системы
Пролог. Приведены основные характеристики отладочных предикатов trace и
spy. Описывается использование предиката statistics для получения
статистической информации.
Написав программу на Прологе, пользователь сталкивается со следующей
проблемой: как заставить ее работать. В большинстве случаев при первой
попытке запуска программы Пролог - система фиксирует состояние неудачи и
возвращает ответ «нет».
Однако такой ответ ничего не объясняет. Необходимо средство для
отслеживания работы программы.
Во многих реализациях Пролога пользователь располагает двумя
методами проверки работы программы:
а) трассировка;
б) контрольные точки.
7.1.Трассировка
Трассировка позволяет пользователю наблюдать за ходом выполнения
программы во время ее работы. При попытке Пролога доказать некоторое
целевое утверждение на экране появляется сообщение, содержащее имя цели и
состояние связанных с ней параметров. Предположим, мы написали
следующую программу:
степень (N,P):-integer (N),
integer (P),!,
ст(N,P,A),
write (N),
write ('возведенное в степень'),
write (P),
write (‘есть'),
write (A),
nl.
степень (_,_):-write ('оба параметра не являются целыми').
степ (N,0,1):-!
степ (N,P,R):-M is P-1,
степ(N,M,Q),
R is Q*N.
61
Теперь мы выдаем запрос степень(2,4) и ожидаем получить ответ «16».
Однако система отвечает «нет». Для получения информации об ошибках в
программе мы рекомендуем использовать трассировку.
Опуская вопрос включения механизма трассировки, остановимся на
рассмотрении информации, которая будет отображаться каждый раз при вводе
целевого утверждения.
Снова напечатаем степень(2,4). На этот раз на экране появятся
следующие сообщения:
CALL: степень (2,4)
CALL: integer (2)
CALL: integer (4)
CALL: ст (2,4,A)
FAIL: ст (2,4,A)
нет.
Сообщение означает, что наша программа работала нормально, но при
попытке согласовать целевое утверждение ст(2,4,А) возникло состояние
неудачи. При проверке оказалось, что утверждение ст(2,4,А) набрано ошибочно
вместо утверждения степ(2,4,А). Исправим программу и повторим запуск.
Сопоставление утверждения степ(2,4,А) проходит успешно и программа
продолжает работать. Допустим, что больше ошибок нет и мы получаем
верный ответ «16».
Другой часто встречающейся ошибкой является неверное задание числа
параметров у предиката. Предположим, мы записали программу таким образом:
степень (N,P):-inleger(N),
integer(P),
степ(М,Р),
write(N),
write(‘возведенное в степень'),
write (P),
write('ecть'),
write (A),
nl.
степень (_,_):-write (‘ оба параметра не являются целыми числами'), nl.
степ (N,0,1):-!
cтeп(N,P,R):- M is P-1,
степ(N,M,Q),
R is Q*N.
Тогда попытка сопоставить целевое утверждение степ(N,P) арности два с
62
процедурой степ арности три закончится неудачей. Как и раньше, будет
получен ответ «нет». Ошибку можно найти, воспользовавшись механизмом
трассировки, как это было сделано в предыдущем примере.
Мы рассмотрели случаи поиска простых ошибок. Тот же метод трассировки
применяется и для сложных программ, содержащих возвраты и рекурсию.
Включение и выключение механизма трассировки
При загрузке системы Пролог трассировка обычно выключена. Механизм
включения трассировки зависит от реализации системы Пролог. Ниже описаны
два типичных метода включения механизма трассировки.
1. Использование механизма прерывания с клавиатуры, например
одновременное нажатие клавиш CTRL и Т. Повторное нажатие клавиш CTRL и
Т приводит к отключению трассировки. Программист может использовать
данный метод при разработке программы за терминалом.
2. Использование предиката trace для включения режима трассировки и
предиката notrace для выключения. К данному методу программист
обращается при проверке отдельных частей программы. Предикат trace нужно
вставить в программу непосредственно перед проверяемым участком, а
предикат notrace - сразу после него. В результате пользователю выдается
информация о трассировке для последовательности целевых утверждений,
находящихся между этими двумя предикатами.
Если происходит повторный запуск системы Пролог после возникновения
ошибки или срабатывания средства прерывания в контрольной точке, режим
трассировки обычно выключается.
Предикаты, содержащие точки трассировки, запоминаются системой и
вызываются вновь при включении соответствующего режима трассировки.
Необязательные параметры трассировки
Помимо уже описанных общих параметров трассировки, некоторые
реализации Пролога поддерживают дополнительные параметры. Благодаря им
пользователь имеет возможность подключать точки трассировки к любому
предикату в своей программе. После присоединения точек трассировки
пользователь может включать режим трассировки. В результате информация о
трассировке будет выводиться на терминал (или устройство печати) только в
том случае, когда при выполнении программы на Прологе предикаты с точками
трассировки становятся целевыми утверждениями. Количество информации,
появляющейся на дисплее, существенно сокращается: отображаются только
предикаты, представляющие интерес для пользователя.
63
Предикаты трассировки
Встроенными обычно бывают следующие предикаты трассировки:
trace (F)
Подключает точки трассировки ко всем процедурам с
главным функтором F.
Подключает точки трассировки ко всем процедурам с
главным функтором F и арностью А.
trace ([F/A,F1/A1,...])
Подключает точки трассировки к списку процедур с
функторами F, F1 и арностью А, А1 соответственно.
notrace
Удаляет точки трассировки из всех процедур с
главным функтором F.
notrace (F/A)
Удаляет точки трассировки из всех процедур с
главным функтором F и арностью А.
notrace ([F/A,F1/A1),...J) Удаляет точки трассировки из всех процедур
с функторами F, F1 и арностью А, А1 соответственно.
notraceall
Этот предикат удаляет все точки трассировки,
существующие в базе данных Пролога.
trace (F/A)
Режимы трассировки
Некоторые реализации Пролога позволяют пользователю выбирать тот
режим трассировки, при котором в процессе выполнения целевого утверждения
Пролога информация выдается в одной или нескольких точках CALL , FAIL,
REDO и EXIT*.
Наличие таких средств также позволяет пользователю более гибко
управлять количеством информации, выдаваемой на терминал.
7.2. Установка контрольных точек
В процессе отладки иногда возникает необходимость приостановить
работу программы при доказательстве каждой цели. В Прологе для этого
имеется средство, называемое методом контрольных точек.
Применение контрольных точек дает возможность пользователю
остановить выполнение программы при переходе к новой цели, проверить
состояние параметров и, возможно, воздействовать на дальнейший процесс
доказательства.
Метод контрольных точек аналогичен пошаговому режиму выполнения
команд, применяемому при разработке программ на языке Ассемблера.
Возможные действия в контрольной точке
Возможности, имеющиеся в контрольной точке, зависят от реализации
Пролога, но в общем их можно свести к следующим:
64
а (abort/прекратить) - вернуться на верхний уровень интерпретатора;
b (backtrace/вернуться назад) - показать последние N выполненных
целевых утверждений;
f (fail/неудача) - установить результат «неудача» при доказательстве
текущей цели;
s (succeed/успех) - установить результат «успех» при доказательстве
текущей цели.
Включение и выключение режима контрольных точек
При первой загрузке системы Пролог режим установки контрольных
точек обычно бывает отключен. Впоследствии он может быть включен одним
из способов, зависящих от реализации Пролога.
Приведем два типичных метода включения режима контрольных точек.
1. Использование механизма прерывания с клавиатуры, например
одновременное нажатие клавиш CTRL и S. Повторное нажатие клавиш CTRL и
S приводит к отключению режима контрольных точек. Программист имеет
возможность применить данный метод при разработке программы за
терминалом.
2. Применение предиката spy для включения режима контрольных точек
и предиката nospy для выключения. Данный метод служит для проверки
отдельных частей программы. Предикат spy необходимо вставить в программу
непосредственно перед проверяемым участком, а предикат nospy - сразу после
него.
В результате выполнение программы будет приостанавливаться на
каждом целевом утверждении из последовательности, находящейся между
этими двумя предикатами.
Если происходит повторный запуск Пролог-системы после возникновения ошибки или выполнения операции прерывания в контрольной
точке, режим контрольных точек обычно отключается.
Предикаты, содержащие контрольные точки, могут запоминаться
системой и вызываться при включении соответствующего режима.
Необязательные параметры режима контрольных точек
Кроме общих параметров режима контрольных точек, рассмотренных
ранее, некоторые реализации Пролога поддерживают специальные параметры.
Если это необходимо, пользователю разрешается подключать контрольные
точки к любому предикату в своей программе. После присоединения
контрольных точек пользователь включает режим контрольных точек, который
прерывает выполнение программы только в тот момент, когда целевыми
утверждениями контрольными точками. Благодаря этому программы на
Прологе выполняются обычным образом (за исключением скорости
65
выполнения) между целевыми утверждениями с контрольными точками.
Предикаты для работы с контрольными точками
spy(F)
spy(F/A)
spy([F/A,Fl/Al,...])
nospy(F)
nospy(F/A)
nospy([F/A,Fl/Al,...])
nospyall
Устанавливает контрольные точки для всех процедур
с главным функтором F.
Устанавливает контрольные точки для всех процедур
с главным функтором F и арностью А.
Устанавливает контрольные точки для списка
процедур с функторами F, F1 и арностью А, А1
соответственно.
Удаляет контрольные точки из всех процедур с
главным функтором F.
Удаляет контрольные точки из всех процедур с
главным функтором F и арностью А.
Удаляет контрольные точки из списка процедур с
функторами F, F1 и арностью А, А1 соответственно.
Удаляет все контрольные точки, существующие в
базе данных Пролога.
Режимы, связанные с контрольными точками
В некоторых реализациях Пролога пользователю разрешается выбрать
тот режим контрольных точек, при котором происходит останов в любой из
точек CALL, FAIL, REDO и EXIT при выполнении Пролог-программы. Это
дает возможность пользователю управлять количеством информации,
выдаваемой на терминал.
Основные отладочные предикаты
nodebug
Отключает режим трассировки и режим
контрольных точек.
Статистическая информация
Иногда пользователю требуется следующая информация:
—сколько времени Пролог-программа выполнялась в системе;
—сколько логических выводов совершила система во время выполнения
программы. (Замечание. Логический вывод - выполнение одного целевого
утверждения Пролога.);
—сколько места занимает база данных Пролога ?
Такую информацию обычно можно получить с помощью встроенных
предикатов. Имена предикатов и их параметры зависят от реализации, но, как
правило, они аналогичны именам и параметрам предикатов, описанных ниже.
Встроенный предикат statistics(T,L) конкретизирует переменную Т
значением текущего процессорного времени (обычно в миллисекундах) , а
66
аргумент L - числом произведенных логических выводов. Попытка доказать
целевое утверждение statistics (T,L) всегда заканчивается успехом.
Например, требуется определить, сколько времени понадобится программе
степень, чтобы возвести 50 в степень 4 и выдать ответ. Зададим новый предикат
время:
время(Время,Счет) statistics (T1.L1),
степень (50,4),
statistics (T2.L2),
Время is T2-T1,
Счет is L2-L1.
При первом вызове предиката statistics переменные Т1 и L1 будут
конкретизированы соответствующими значениями. Затем выполняются
предикат степень(N,Р) и определяющие его целевые утверждения. При втором
вызове предиката statistics переменные Т2 и L2 конкретизируются новыми
значениями, полученными после выполнения предиката степень(N,Р).
Процессорное время, необходимое для выполнения, составляет Т2-Т1
миллисекунд, а количество вызванных целевых утверждений равно L2-L1.
Число логических выводов в секунду составляет
1000*L2-L1/T2-T1.
Встроенный предикат core выводит в текущий выходной поток
статистическую информацию, относящуюся к использованию памяти. Эта
информация различается в зависимости от конкретной системы, но
приблизительно такова:
— количество свободного пространства ^оставшегося в базе данных Пролога (в
словах);
— размер локального и глобального стеков (в словах). (Замечание. Локальный
и глобальный стеки - динамические внутренние структуры данных,
используемые в большинстве реализаций Пролога.)
Приложение A
Синтаксис
В приведенном ниже описании синтаксиса встречаются терминальные
символы (те, из которых состоит реальная программа). Они отличны от слов,
начинающихся со строчной буквы, от специальных символов ::- и
комментариев, взятых в круглые скобки. Приняты следующие соглашения:
67
текст, находящийся в угловых скобках, может появляться несколько раз, а
различные варианты начинаться каждый с новой строки (это не относится к
комментариям).
А.1. Синтаксис программ
программа ::= предложение <предложение>
предложение ::=
утверждение
управляющая команда
утверждение ::=
голова.проб_симв
голова :- хвост.проб_симв
управляющая команда ::= ?- целевое утверждение
<, целевое утверждение
проб_символ (см. примеч.1)
голова ::= целевое утверждение
хвост ::= целевое утверждение
<, целевое утверждение>
(см.примеч.1)
целевое утверждение ::= атом
структура
проб_символ ::=
символ табуляции,
пробел, возврат каретки
Примечание 1. Переменная (скажем, X) может выступать в качестве
целевого утверждения в управляющей команде или в хвосте, но при этом она
интерпретируется как целевое утверждение call(X).
А.2. Синтаксис термов
терм::=
константа
переменная
структура
константа::=
атом
число
переменная::=
загл_буква <альфа>
68
подчеркивание <альфа>
подчеркивание
структура::=
главный_функтор (компонента
<,компонента>)
главный_функтор ::=атом
компонента ::=терм
атом ::=
строчн_буква <альфа>
знак <знак>
резерв_слово
<атом_симв>'
резерв_слово ::= (см. А.5)
атом_симв ::= символ (за исключением кавычки -')
загл_буква ::=
(любая из следующих букв: A B C
DEFGHIJKLMNOPQRS
Т U V W X Y Z)
подчеркивание ::= (символ подчеркивания)
строчн_буква ::=
(символ из следующих букв: a b
cdefghIjklmnopq
r s t u v w x y z)
альфа ::=
(любая из цифр: 0 1 2 3 4 5 6 7 8 9)
буква ::=
загл_буква
строчн_буква
знак::=
(любой из следующих символов:
+-*/\^=‘~:.?@#$&)
число::=
целое
действительное
целое ::=
цифра <цифра>
символ ::= (любой символ ASCII)
действительное::=
целое_со_знаком Е целое_со_знаком
целое_со_знаком.целое
целое_со_знаком .целое Е целое_со_знаком
69
целое_со_знаком ::=
целое
+целое
–целое
А.З. Синтаксис списка
прав_постр_список::=
пустой_список
(голова_списка,хвост_списка)
голова_списка.хвост_списка
[голова_списка I хвост_списка]
[член<,член> I <хвост_списка>]
[член<,член>]
строка
пустой_список ::= []
голова_списка ::= терм
прав_постр_список
хвост_списка ::=
прав_постр_список
член ::=терм
строка ::- "символ_стр"
символ_стр ::=
символ (за исключением")
А.4. Синтаксис комментария
комментарий ::= /* <символ> */
(Последовательность */ не должна встречаться внутри комментария.)
Приложение Б
Встроенные операторы
Приведем список встроенных операторов и их типичное описание.
удаляет все целевые утверждения
процедуры F, имеющие арность N
повторно запускает Пролог-систему
(повторно вводит интерпретатор
верхнего уровня)
abolish(F,N)
abort
70
arg(N,T,A)
N-м аргументом терма Т является А
assert(X)
аналогичен оператору assertz(X)
asserta(X)
компилирует утверждение X и
помещает его перед другими
утверждениями в эту же процедуру
assertz(X)
компилирует утверждение X и
помещает его после других утверждений
в эту же процедуру
atom(X)
X является атомом
atomic(X)
X является атомом, целым числом
или строкой
break_handler
временное использование терминала
для ввода/вывода
call(X)
попытка доказать терм X как целевое
утверждение
clause (X,Y)
существует утверждение с головой
X и телом Y
clause_of (F,A,C)
С является утверждением с
функтором F и арностью А
close (S)
закрывает поток S
compare(Op,X,Y)
Ор является результатом сравнения
термов X и Y
consult (F)
файл F компилируется и добавляется
к программе
date (L)
возвращает список L в виде
[День,Месяц,Год]
date_and_time (L)
возвращает список L в виде
[День,Месяц,Год,Часы.Милуты, Секунды]
display (X)
печатает терм Х в префиксной форме в
текущий выходной поток
редактирует файл X и повторно
вводит его в систему
edit(X)
71
endmodule(X)
конец модуля X
extcall(N,Arg)
вызывает внешнюю процедуру N,
передавая ей Arg
fail
вызывает состояние неудачи при
доказательстве целевого утверждения
functor(T,F,A)
функтором терма Т является F с
арностью А
get(X)
переменной X присваивается
следующий вводимый символ, отличный
от пробела
get0(X)
переменной X присваивается
следующий вводимый символ
halt
выход из Пролог-системы и возврат в
операционную систему
import (X,M)
процедура X импортируется из модуля М
integer(X)
X является целым числом
X is Y
X является значением арифметического
выражения Y
length (L,M)
длина списка L равна М (является
обратимым)
listing (X)
выводит все утверждения с атомом X в
качестве имени процедуры
listing (X,M)
аналогичен оператору listing (X), но
сфера его действия ограничена модулем
М
listing(X,M,N)
аналогичен* оператору listing(X,M), но
максимальная арность утверждений
равна N
name(X,Y)
связывает атом X со списком целых
чисел Y (Y имя списка)
name_sub(X,N,Y)
N-м символом X (атома или строки)
72
является Y
nl
применяется для перехода на новую
строку в выходном потоке (при печати на
дисплее)
nl(S)
применяется аналогично nl для потока S
non term (X)
X не является составным термом
nonvar(X)
переменная X является
конкретизированной
not(X)
попытка доказательства предиката
заканчивается успехом, если
доказательство X заканчивается
неудачей
op(P,T,A)
определяет оператор А, имеющий тип Т
и приоритет Р
open(F,S,M)
открывает файл F в поток S с режимом
М
М = read определяет режим чтения,
М = write - режим записи
put(X)
выводит символ X
read(X)
вводит следующий терм и присваивает
его в качестве значения переменной X
read(X,Y)
вводит следующий терм, присваивает
переменной X значение введенного
терма и сопоставляет его с Y
readb(X)
вводит следующий терм X. Ввод
символа новой строки прекращает ввод
терма
readb(X,S)
вводит следующий терм X из потока S.
Ввод символа новой строки прекращает
ввод терма
readline(X)
читает строку на экране и присваивает
ее в качестве значения строке X
readline(X,S)
читает строку в потоке S и присваивает
73
ее в качестве значения строке X
reconsull(F)
компилирует файл F в программу,
уничтожает старые утверждения
repeat
всегда заканчивается успехом
reslore(X)
восстанавливает состояние системы из
файла X
retract(X)
удаляет из базы данных утверждение,
сопоставимое с X
retractall(X)
удаляет из базы данных все
утверждения, сопоставимые с X
save(X)
записывает состояние системы в файл X
see(X)
открывает файл X
seeing(X)
текущим входным потоком является X
seeing(X,L,C)
аналогичен оператору seeing(X), но
также указывает строку/столбец в файле
seen
закрывает текущий входной файл
skip(X)
пропускает символы в текущем входном
потоке, пока не встретится символ,
сопоставимый с X
statistics(X,Y)
определяет время обработки и число
логических выводов
string (X)
X является строкой
string_name(A,L)
связывает строку А со списком целых
чисел L
succ(X,Y)
за целым числом X следует целое число
Y
tab(X)
записывает X пробелов в текущий
выходной поток
tell(X)
изменяет текущий выходной поток на X
74
telling (X)
X является текущим выходным потоком.
time(L)
L является списком [Часы,Минуты,
Секунды]
told
закрывает текущий выходной поток.
Текущим выходным потоком становится
дисплей ("user")
true
всегда заканчивается успехом
unknown (X,Y)
определяет действие в том случае, если
встретится неизвестная процедура
var(X)
переменная X не конкретизирована
visa(X,Y)
содержащаяся внутри модуля процедура
Y имеет атрибут X
write (X)
записывает терм Х
write(S,X)
выводит X в поток S
writedepth(O,N)
изменяет максимальную вложенность
глубины печати от 0 до N
writeq(X)
записывает терм X, заключая его в
кавычки, если это необходимо
writeq(S.X)
выводит X в поток S, при необходимости
заключая X в кавычки
writewidth(0,N)
изменяет максимальную ширину строки
(в текущем потоке) от 0 до N
X==..Y
Y является покомпонентным списком
структуры X
!
устраняет альтернативный выбор
X=Y
термы X и Y сопоставимы
X\=Y
термы X и Y не сопоставимы
X==Y
термы X и Y идентичны
75
X \==Y
термы X и Y не идентичны
X<@Y
X находится перед Y в стандартном
порядке
X>@Y
X находится после Y в стандартном
порядке
X<@=Y
X находится не после Y в стандартном
порядке
X>@=Y
X находится не перед Y в стандартном
порядке
X<Y
X меньше Y, при условии, что X и Y целочисленные выражения
X<=Y
X меньше или равен Y, при условии, что
X и Y - целочисленные выражения
X>Y
X больше Y, X и Y - целочисленные
выражения
X>=Y
X больше или равен Y, X и Y целочисленные выражения
X=:=Y
выражения X и Y эквивалентны, X и Y целочисленные выражения
X -\- Y
выражения X и-Y не эквивалентны
(для целочисленных выражений)
[X]
транслирует файл в список имен
файлов
,
;
конъюнкция
дизъюнкция
Предметный указатель
Анонимная переменная 3,5
Арифметические операторы 12,14
Арифметические списковые выражения 13
Арифметические выражения 12,13,15
Арность 5,10,44
Ассоциативность оператора 45
76
База данных 51,66
Бинарное дерево 38
Встроенные предикаты 6,50
Входной поток 58
Выходной поток 57,71
Главный функтор 6,10
Голова 18,29
Граничные условия 19
Именованная переменная 5
Компоненты 5
Контрольная точка 63
Константа 3
Левоассоциативный 48
Отладка 61
Параметры трассировки 64
Переменная 3
Позиция оператора 45
Правило 6
Правоассоциативный 48
Предложение 6,68
Представление множеств 39
Принадлежность множеству 40
Приоритет оператора 45
Приоритетный номер 46
Программный модуль 50
Прямая трассировка 65
Рекурсия 16
Ряд Фибоначчи 27
Свободная переменная 5
Синтаксис операторов 44
77
Синтаксис Пролога 9
Спецификаторы 56
Списки 29
Структура 29
Термы 3
Унификация 9
Утверждения 6
Факт 6
Хвост 29
78
Скачать