3. Структура программ Турбо-Пролога

       Существует множество реализации языка Пролог для разных классов вычислительных систем. В этом пособии за основу принимается программный продукт компании Borland International 'Турбо-Пролог" для IBM-совместимых компьютеров. Программа на Турбо-Прологе состоит из нескольких секций, каждая из которых идентифицируется ключевым словом и имеет следующую обобщенную структуру:
domains
/* секция объявления доменов */
database
/* секция объявления динамических баз данных */
predicates
/* секция объявления предикатов */
goal    
/* подцель_1, подцель_2, и т. д. */
clauses
/* предложения (факты и правила) */
       Обязательным в программе является присутствие двух секций с именами predicates и clauses. В первой из них описываются структуры используемых в программе отношений, а во второй эти отношения опред(деклnbsp;    Для набора фактов и правил, рассмотренных выше, один из возможных примеров программы на Турбо-Прологе будет иметь вид:

/* программа 1 */
predicates
likes(string,string)
clauses
likes ("Иван","Марья").
likes ("Петр","пиво")
likes ("Иван",X) if likes ("Петр",X)
 
      
/* описание предиката */
      
/* факт */
/* факт */
/* правило */
       В этой программе предикат likes описывает структуру, отношения, домены которого имеют тип строки символов. Факты и правила записаны в виде предложении Пролог-программы, каждое из них заканчивается точкой, а текст заключенный в /* ... */ - это комментарии. Отсутствие в программе секции goal предполагает, что запросы к программе будут осуществляться из оболочки Турбо-Пролога. Так, например, при запросе о поиске всех любителей пива, цель должна быть сформировала в окне диалога Турбо-Пролога в виде:
Goal: likes(Who,"пиво").
       В ответ на этот запрос в том же окне диалога Турбо-Пролог выведет всех тех, кто любит пиво, связывая их с переменной Who.

       Однако использование в Пролог-программах только двух секций, т.е. predicates и clauses, является достаточным только для простейших программ, которые не используют описание данных и их структур, не работают с динамическими базами данных. Рассмотрим более подробно назначение каждой из секций программы.

3.1. Секция domains Пролог-программы

       В секции domains объявляются любые нестандартные домены, используемые для аргументов предикатов. Домены в Прологе являются аналогами типов в других языках. Основными стандартными доменами Турбо-Пролога являются:

       char - символ, заключенный в одиночные кавычки (например, 'а');
       integer - целое от -32768 до 32767 (переводится в вещественное автоматически, если необходимо);
       real - вещественное (например, -68.72, бе-94, -791е+21);
       string - последовательность символов, заключенных в двойные кавычки (например, "нажмите ввод");
       symbol - либо набор латинских букв, цифр и символов подчеркивания, в котором первый символ - прописная буква ( например, n_fax ) ; либо последовательность символов, содержащая пробелы или начинающаяся со строчной буквы, заключенная в кавычки (например, "Список СУБД").
       file - символическое имя файла, которое начинается с прописной буквы.

       Кроме стандартных доменов пользователь может использовать свои. Для этого в области объявления доменов можно использовать следующие форматы:

а) name = stanDom ,
где stanDom - один из стандартных доменов: int, char, real, string или symbol, name - одно или несколько имен доменов. Например, fio = symbol или year, height = integer ;
б) mylist = elementDom* ,
где mylist - область, состоящая из списков элементов из области elementDom, которая может быть определена пользователем или иметь стандартный тип.Например, numberh5t = integer* или letter = char* ;
в) myCompDom = functor1(d11,…,d1n) ; functor2(d21,..., d2n) ; ... functorm(dm1,...,dmq) ,
где myCompDom - область, которая состоит из составных объектов, описываемых указанием функтора и областей для всех компонентов. Правая часть такого описания может определять несколько альтернатив, разделенных ";" или "or". Каждая альтернатива должна содержать единственный Функтор functor, и описание типов для компонентов dij.Например,
auto = car(symbol,integer) ,
packing = box(integer, integer, integer) ; bottle(integer)
описывает две области auto и packing. Область auto соответствует двухкомпонентной структуре с функтором car, а область packing соответствует одной из двух возможных структур box и bottle, которые различаются не только именами, но и количеством компонент.
г) file = name1; name2 ; ... namen
используется, когда пользователю необходимо ссылаться на файлы по их символическим именам.

       Пример. Требуется разработать структуру данных для хранения информации о компьютерах. При этом каждый компьютер будет рассматриваться как набор входящих в него устройств, среди которых могут быть: процессоры с указанием их наименования и частоты, НЖМД с указанием их объема и фирмы изготовителя, также мониторы определенного типа. Область определения доменов для этого примера будет иметь вид
domains
name , firm , type = symbol
freq , vol = integer
device = processor(name,freq);disk(firm,vol);monitor(type)
computer = device*
       В этом описании домен computer является списком элементов типа device, то есть каждый элемент этого списка может иметь структуру типа либо processor, либо disk, либо monitor, содержащих одну или две компоненты, каждая из которых имеет стандартный символьный ют целый тип.

3.2. Секция predicates

       В секции predicates объявляются предикаты и типы (домены) аргументов этих предикатов. Имена предикатов должны начинаться со строчной латинской буквы, за которой следует последовательность букв, цифр и символов подчеркивания (до 250 знаков). В именах предикатов нельзя использовать символы пробела, минуса, звездочки, обратной (и прямой) черты. Объявление предикатов имеет форму:
predicates
predicateName1 (domen11, domen12,..., domen1m)
predicateNamen (domenn1, domenn2,..., domennk)
       Здесь domenij - либо стандартные домены, либо домены, объявленные в секции domains. Объявление домена аргумента и описание типа аргумента - суть одно и то же. Количество доменов (аргументов) предиката определяют арность (размерность) предиката. Предикат может не иметь аргументов и указываться только именем. Обычно выбирается такое имя предиката, чтобы оно отражало определенный вид взаимосвязи между аргументами предиката. Пример описания предикатов:
predicates
student( string,real)
start
good_student( string)
       Можно использовать несколько описаний одного и того же предиката. При этом все описания должны следовать одно за другим и должны иметь одно и то же число аргументов. Пусть требуется определить отношение между тремя аргументами, первые два из которых соответствуют слагаемым, а третий - сумме двух первых. Этот предикат может быть описан в следующем виде
predicates
add(integer,integer,integer)
add(real,real,real)
и позволит его аргументам принимать значения как из области целых, так и действительных чисел.

3.3. Секция clauses

       В секции clauses размещаются факты и правила, с которыми будет работать Турбо-Пролог, пытаясь разрешить цель программы.

       Факт - это утверждение о существовании некоторого отношения между аргументами, обозначаемого именем предиката. Факты - это фразы без условий и они содержат утверждения, которые всегда абсолютно верны. Форма записи фактов:
clauses
predicateName1 (term11, term12,..., term1к). ,
...
predicateNameN (termN1, terniN2,..., termNL).
где predicateNamel - имена предикатов, описанных в секции predicates , a term11, ... , term11 - аргументы предикатов (термы), количество которых должно соответствовать арности описания предиката. Пример фактов, определяющих отношение, заданное предикатом student, может иметь вид:
clauses
student("Петров" ,4.5).
student("Сидоров" ,3.75).
       Правила содержат утверждения, истинность которых зависит от некоторых условий (подцелей), образующих тело правила. В Прологе правила представляются в виде
Заголовок :- Подцель1 , Подцель2 , ... , ПодцельN . ,
где обозначение :- читается как "если", а Заголовок имеет такую же форму, как и факт. Тело правила - это список подцелей, разделенных запятыми. Запятая понимается как конъюнкция и читается как "и". Пример записи правила
good_student( Name ) :- student( Name , В ) , В > 4.
       Для того, чтобы заголовок правила оказался истинным, необходимо, чтобы каждая подцель, входящая в тело правила, была истинной. Переменные в заголовке правила, квантифицированы универсально. Это означает, что правило, заголовок которого содержит переменные, будет истинным для любых термов, удовлетворяющих подцелям правила. С другой стороны переменные, входящие только в тело правила, квантифицированы экзистенциально. Если учесть такую квантификацию, то приведенное выше правило можно прочитать так:
Для любого лица Name, Name является хорошим студентом
ЕСЛИ   существует средний балл В такой, что Name является студентом со средним баллом В
Исредний балл В больше 4.
Множество правил, заголовки которых содержат одинаковые имена предикатов и количество аргументов, называются процедурой. В Прологе предикаты определяются (реализуются) при помощи процедур. Так следующие два правила
max( Х , Y , X) :- Х >=Y.
max( Х , Y , Y) :- Х < Y.
реализуют процедуру нахождения наибольшего из двух чисел, определяемую предикатом вида max(number1, number2, max_number). Считается, что между этими двумя правилами неявно присутствует соединительный союз "или".

3.4. Секция goal

       В секции goal задается внутренняя цель программы. Это позволяет программе запускаться независимо от среды разработки. Если внутренняя цель включена в программу, то Турбо-Пролог выполняет поиск только одного первого решения, и связываемые с переменными значения не выводятся на экран.

       Если внутренняя цель не используется, то в процессе работы есть возможность вводить в диалоговом окне внешнюю цель. При использовании внешней цели Турбо-Пролог ищет все решения и выводит на экран все значения, связываемые с переменными.

       В систему Турбо-Пролог включено более 200 встроенных стандартных предикатов и более дюжины стандартных доменов. В случае использования этих предикатов и доменов нет необходимости объявлять их в программе.

       Рассмотрим пример программы, в которой задана внутренняя цель и используется обращение к стандартным предикатам:
/* Программа 2 */
predicates
hello
goal     
hello.
clauses
hello :-
makewindow(l,7,7,"My first programm",4,54,10,22),
nl, write("Please, type your name "),
cursor(4,5),
readln(Name) ,nl,
write(" Welcome ",Name).
      В этой программе формируется на экране окно, заданного размера и цвета, запрашивается Ваше имя, а затем оно выводится на экран.

      Перечень и назначение стандартных предикатов приведен в Приложении.

      Однако чаще всего целью является сложный запрос к программе. Для разрешения какой-либо сложной цели Пролог должен разрешить все его подцели, создав при этом необходимое множество связанных переменных. Если же одна из подцелей ложна. Пролог возвратится назад и просмотрит альтернативные решения предыдущих подцелей, а затем вновь пойдет вперед, но с другими значениями переменных. Этот процесс называется "поиск с возвратом".

3.5. Секция database

       Ключевое слово database указывает на начало последовательности описаний предикатов динамической базы данных. Динамическая база данных является базой, в которую факты добавляются во время исполнения программы. Требования к описаниям предикатов такие же, как и в секции predicates. Факты, принадлежащие динамической базе данных, обрабатываются отличным от обычных предикатов образом для того, чтобы ускорить работу с БД большого объема. Факты динамической базы могут модифицироваться в течение сеанса работы, загружаться из дискового файла с помощью стандартного предиката consult или записываться в дисковый файл с помощью предиката save.