С и с т е м а л о г и ч е с к о г о п р о г р а м м и р о в а н и я P R O L O G U S Р у к о в о д с т в о п о л ь з о в а т е л я С о д е р ж а н и е 1. Введение 2. Синтаксис языка 2.1. Используемые обозначения 2.2. Алфавит языка 2.3. Числа, символьные константы и переменные 2.4. Термы 2.5. Факты, правила и запросы 3 Семантика языка 3.1.Операция унификации 3.2.Логический вывод 4. Встроенные предикаты 4.1. Арифметические предикаты 4.2. Предикаты сравнения 4.3. Предикаты проверки типов значений 4.4. Предикаты преобразования 4.5. Предикаты ввода-вывода термов 4.6. Предикаты управления логическим выводом 4.7. Предикаты работы с динамической базой 4.8. Графические предикаты 4.9. Предикаты обработки символьных констант 4.10. Прочие предикаты 5. Работа с системой PROLOGUS 5.1. Запуск системы 5.2. Главное меню 5.3. Работа с файлами 5.4. Редактирование файла 5.5. Поиск строки 5.6. Запуск выполнения программы 5.7. Трассировка программы 5.8. Установка опций работы системы 1. Введение В этом руководстве описывается учебная система логического программирования PROLOGUS. В первую очередь рассмотрены элементы языка Пролог, реализованного в системе PROLOGUS. Подробно обсуждаются типы данных, операции, средства управления ходом выполнения программы, встроенные предикаты. Дается также руководство по работе пользователя с системой. Система PROLOGUS включает интерпретатор языка Пролог, текстовый редактор с контекстной подсветкой, трассировщик и контекстно-зависимую систему подсказки. Работа с системой основана на меню. Язык логического программирования Пролог относится к классу декларативных языков и тем самым коренным образом отличается от традиционных языков программирования. В традиционных языках программирования базовой операцией является присваивание значения переменной, а базовыми конструкциями, управляющими ходом выполнения программы - условный оператор и цикл. В Прологе же используется более общий механизм присваивания, известный как унификация. Унификация может быть успешной или неуспешной. В случае успеха оба унифицируемых терма могут измениться, при этом они станут эквивалентными. Кроме того, после унификации две переменные могут оказаться взаимосвязанными. В Прологе ход выполнения программы задается путем упорядочения предложений в программе и подцелей внутри предложений. Согласование некоторой подцели не начинается прежде, чем будет закончено согласование предыдущей подцели. Такая стратегия выполнения называется поиском сначала-вглубь. Чтобы цель была успешно согласована, она должна унифицироваться с головой какого-либо предложения и при этом все подцели в теле данного предложения должны быть успешны. Если данное условие не выполняется, цель неуспешна. В этом случае производится возврат к предыдущему этапу (бектрекинг) и делается попытка согласовать данную цель повторно и получить альтернативное решение. Таким образом, базовыми операциями, влияющими на ход выполнения программы на Прологе, являются унификация и бектрекинг, причем в отличие от традиционных языков программирования, они присутствуют в программе неявно. Эти операции подробно рассматриваются в разделе 4. Есть еще одно важное отличие Пролога от традиционных языков программирования. В последних при определении процедур или функций всегда четко определяется, какие аргументы являются входными, какие - выходными. В Прологе же один и тот же аргумент может в одних случаях выступать как входной, а в других - как выходной. Поэтому можно, например, одну и ту же процедуру использовать в одних случаях для склеивания списков, а в других - для разъединения списков. 2. Синтаксис языка 2.1. Используемые обозначения В описании синтаксиса языка Пролог используются следующие обозначения: - фигурные скобки заключают в себе конструкции, которые могут повторяться нуль или более раз; - квадратные скобки заключают конструкции, которые могут либо присутствовать, либо отсутствовать (за исключением синтаксиса списков, где квадратные скобки обозначают сами себя, то есть терминальные символы языка). 2.2. Алфавит языка <символ>::=<буква>|<цифра> | <специальный символ> | <произвольный символ> <специальный символ>::= ( | ) | [ | ] | , | . | ; | :- | ? | ?- | " | ' | -- Буквы могут использоваться латинские и русские, прописные и строчные. Кроме того, к буквам относится символ подчеркивания (_). Под термином <произвольный символ> понимается любой символ, имеющийся в наборе символов клавиатуры, отличный от букв, цифр и указанных специальных символов, под термином <символ параметра> подразумевается символ, зарезервированный как начальный символ имени переменной (параметра). В системе PROLOGUS символ параметра - это подчеркивание. Последовательность символов, начинающаяся спецсимволом '%', считается комментарием. Комментарий заканчивается в конце той строки текста программы, в которой он начат. Таким образом, комментарий может идти либо отдельной строкой, либо в конце строки. Кроме того, в качестве начала комментариев могут использоваться символы '$', '@', '&'. Символ пробела может вставляться в произвольные места текста за следующими исключениями: он не должен входить в числа и идентификаторы. 2.3. Числа, символьные константы и переменные Минимальными неделимыми единицами текста являются числа, символьные константы и переменные. 2.3.1. Числа Числа в данной версии языка используются только неотрицательные, однако допускается, чтобы результатами вычислений были отрицательные числа. Числа могут быть целые и с плавающей точкой. Максимальное значение целого числа -- 32767. <целое число> ::= <цифра> { <цифра> } <число с плавающей точкой> ::= [<целое число>].<целое число> [<показатель>] <показатель> ::= E<число> 2.3.2. Символьные константы Символьные константы не делятся на составляющие их символы (их еще иногда называют атомами). Символьная константа, содержащая символы, отличные от букв и цифр и не начинающаяся с буквы, должна заключаться в апострофы. <символьная константы> ::= <идентификатор> | '<символ> { <символ> } ' <идентификатор> ::= <буква> {<буква или цифра>}| <буква или цифра> ::= <буква> | <цифра> Примеры символьных констант: alpha, Иванов, r125, фDlоБПМ, '+', 'А+В', '**', '100' Примеры последовательностей символов, не являющихся символьными константами: A+B, *, R-25, 100, (альфа). Замечание: Если символьная константа начинается с символа "#", она считается эквивалентной символьной константе, содержащей те же символы, что и данная, но без символа "#", т.е. следующие символьные константы будут эквивалентны: '#abcde' и abcde Смысл использования этого вспомогательного символа рассмотрен в подразделе 4.9. Максимальная длина символьной константы 80 символов. 2.3.3. Переменные Переменные обозначаются идентификаторами, начинающимися с символа параметра (подчеркивание). <переменная> ::= <символ параметра>[<идентификатор>] Примеры обозначений переменных: _X _12 _переменная _Сommon_sum Одиночный символ параметра обозначает так называемую "безымянную" переменную, которой по умолчанию присваивается некоторое имя, неизвестное пользователю. Каждое вхождение такой безымянной переменной в текст обозначает другую переменную. Безымянные переменные используются в тех случаях, когда значение, присвоенное переменной, несущественно и не используется в дальнейших вычислениях. Замечание. Способ обозначения переменных и символьных констант в системе PROLOGUS отличается от спосба, принятого во ногих других реализациях языка (например, в системе Arity/Prolog). В тех системах переменные могут начинаться как с символа подчеркивания6 так и с прописных букв, а символьные константы (не взятые в апострофы) могут начинаться только со строчных букв. В системе же PROLOGUS переменные всегда начинаются с символа подчеркивания, а символьные константы огут начинаться с прописных букв. (Это сделано для того, чтобы учащиеся могли записывать символьные константы, выражающие имена людей и прочие имена собственные, не беря их в апострофы.) 2.4. Термы Из чисел, символьных констант и переменных строятся более сложные конструкции, называемые термами. <терм> ::= <число> | <символьная константа> | <переменная> | <сложный терм> | ( <терм> ) <сложный терм> ::= <структура> | <выражение> | <список> | <строка> 2.4.1. Структуры Семантически в языке Пролог есть только один вид сложного терма - структура. Выражения, списки и строки являются всего лишь другими, в некоторых случаях более естественными, способами записи структур. Структура состоит из имени, называемого функтором структуры, и некоторого множества элементов, в качестве которых могут выступать любые термы, в том числе и другие структуры. <структура> ::= <функтор> ([<список элементов>]) <список элементов> ::= <терм> { , <терм> } <функтор> ::= <символьная константа> Примеры структур: 1)str(1,2,3) 2)город(Москва,Россия) 3) '+'(_А, '*'(_В,_С) ) 4) человек(_имя,_фамилия,_год) Структуры удобно изображать в графической форме: 1) str ! --------------------- ! ! ! 1 2 3 2) город ! ------------- ! ! Москва Россия 3) '+' ! --------------- ! ! А '*' ! ---------------- ! ! В С На последнем рисунке хорошо видно, что структура (3) является структурой с функтором '+' и двумя элементами, второй из которых является, в свою очередь, структурой с функтором '*' и двумя элементами. 2.4.2. Выражения Выражения строятся по правилам, близким к правилам построения арифметических и логических выражений процедурных языков. Выражения строятся из символьных констант, определенных как имена операций, и термов, выступающих в роли операндов. Заметим, что термин <операция> здесь используется в достаточной степени условно, так как не всегда с символьной константой, "определенной как операция" связывается какая-либо действительная операция, преобразующая значения операндов в новое значение. <выражение> ::= <операция> <терм> | <терм> <операция> | <терм> <операция> <терм> Операции подразделяются на встроенные, зафиксированные в определении языка, и определенные пользователем в программе на языке Пролог. Каждая операция характеризуется тремя атрибутами: обозначением, видом (префиксные, постфиксные или инфиксные) и приоритетом. Префиксные и постфиксные операции имеют один операнд, инфиксные - два. Для обозначения операций используются символьные константы, причем такую символьную константу можно не заключать в апострофы, т.е. писать А+В вместо А '+' В. Если операция обозначается символьной константой из букв и цифр, то необходимо отделять ее от операндов по крайней мере одним пробелом. Преобразование выражений в структуры производится по следующим правилам: 1) Если op - операция, Т,Т1,Т2 -термы, то конструкции op Т и Т op эквивалентны структуре op(Т), конструкция Т1 op Т2 эквивалентна структуре op(Т1,Т2). 2) Сначала производится преобразование в структуры термов в самых внутренних скобках, затем в более внешних, например: (A+B)*(C-D) эквивалентно '*'('+'(A,B),'-'(C,D)) 3) В случаях, когда скобки не полностью определяют порядок преобразования выражений в структуры, используются приоритеты операций. Сначала преобразуются в структуры операции низших приоритетов. Список встроенных операций и их приоритетов приводится ниже. Специальные операции Операция Приоритет :- 255 ; 253 , 251 СЛЕД (spy), НЕСЛЕД (nospy) 251 =.. 50 ЕСТЬ (is) 40 Операции сравнения Операция Приоритет = \= == \== < =< > >= 40 Арифметические операции Операция Приоритет + - 30 * / 20 mod 10 Примеры преобразований с учетом приоритетов операций (структуры изображаются в графическом представлении): 1) A + B * C + D '+' ! --------------- ! ! -------------- D ! ! A '*' ! ------------- ! ! B C 2) M + N mod 100 '+' ! --------------- ! ! M mod ! ------------- ! ! N 100 2.4.3 Списки Списком в языке Пролог называется структура специального вида, она имеет функтор '.' и либо совсем не имеет элементов (такой список называется пустым), либо имеет ровно два элемента, первый из них может быть произвольным термом, а второй - либо списком, либо переменной. Если эта переменная связывается с каким-либо значением, то последнее должно быть списком. Пример: список из трех элементов _X,_Y,_Z, строго говоря, представляется структурой вида: '.'(_X, '.'(_Y, '.'(_Z, '.'()))) '.' ! ------- ! ! _X '.' ! ------- ! ! _Y '.' ! ------- ! ! _Z '.' Поскольку структурное представление списков громоздко и ненаглядно, для изображения списков используется специальный синтаксис. (Здесь квадратные скобки изображают сами себя, а не возможность опускать заключенную в них конструкцию, как обычно.) <список> ::=[]| [ <элементы списка> ] <элементы списка> ::= терм {,<терм> } <конец списка> <конец списка> ::= <пусто> | | <терм> Пустой список обозначается []. Приведенный выше пример в этой записи выглядит как [_X,_Y,_Z]. Специальный символ '|', отделяющий конец списка, играет важную роль в обработке списков языка Пролог. Как было сказано, вторым элементом структуры '.' может быть либо список, либо переменная. Однако запись вида [1,_Y] всегда порождает структуру вида '.' ! --------- ! ! 1 '.' ! --------- ! ! _Y '.' В ней вторым элементом является не переменная _Y, а список, в то же время запись вида [1|_Y] порождает структуру вида '.' ! ------- ! ! 1 _Y Если переменная _Y будет впоследствии связана с некоторым списком, например [2,3], то это породит правильную списковую структуру '.' ! --------- ! ! 1 '.' ! --------- ! ! 2 '.' ! ------- ! ! 3 '.' Заметим, что присваивание того же значения переменной _Y в первом случае породит структуру вида '.' ! ------- ! ! 1 '.' ! ------- ! ! '.' '.' ! ------- ! ! 2 '.' ------ ! ! 3 '.' т.е. не список [1,2,3], а список [1,[2,3]] 2.4.4. Строки Строкой называется специальный вид списка, элементами которого являются целые числа, представляющие ASCII-коды символов. <строка> ::= "" | " <символ> { <символ> } " Строка "" есть пустая строка, она эквивалентна пустому списку. Строка "12345" эквивалентна списку, содержащему коды символов '1' '2' и т.д. Например, если кодом символа '1' является 49, кодом '2' - 50 и т.д., то строка "12345" эквивалентна списку [49,50,51,52,53]. Примечание: Если символ '"' сам должен быть элементом списка, то, как и в других языках, необходимо удваивать его, например, "ОБ""ЕКТ". Строки не следует путать с символьными константами. Отличие между ними состоит в том, что символьная константа - это атомарный, т.е. неделимый элемент данных, тогда как строка - это, вообще говоря, сложный терм, т.к. это - специальный вид списка. 2.5. Факты, правила и запросы Программа на языке Пролог состоит из правил и фактов, часто употребляется обобщенное название фактов и правил - клозы (или предложения). Выполнение программы начинается при вводе запроса. Совокупность фактов, правил и запросов составляет одновременно программу на языке Пролог и исходные данные для ее выполнения. Порядок следования клозов в исходном тексте во многих случаях несущественен, однако не всегда. <факт> ::= <сложный терм> . | <символьная константа> . Примеры фактов: город(Москва,Россия). city(London,Great_Britain). Иван отец Петр. (предполагается, что 'отец' - операция) LENGTH([],0). Примечание: факт, состоящий только из одной символьной константы, трактуется, как структура без элементов, например, факт search эквивалентен факту search(). <правило> ::= <заголовок> :- <список подцелей> . <список подцелей> ::= <подцель> { , <подцель> } <подцель> ::= <сложный терм> | <символьная константа> <заголовок> ::= <сложный терм> | <символьная константа> Примечание 1: Символьные константы, стоящие на местах заголовка правила или подцели, трактуются как структуры без элементов. Примечание 2: Если строка исходного текста содержит несколько клозов, то вслед за точкой, завершающей клоз, должен идти по крайней мере один пробел. После последнего клоза в строке пробел не обязателен. Примеры правил: 1) _X дед _Y :- _X отец _Z, _Z отец _Y. _X дед _Y :- _X отец _W, _W мать _Y. 2) ELEMENT (_X, [_Y|_Z] ) :- _X=_Y. ELEMENT (_X, [_Y|_Z] ) :- ELEMENT(_X,_Z). Совокупность фактов и правил часто называют базой данных Пролога. Их можно рассматривать как описание свойств некоторой предметной области. База данных Пролога в сочетании с запросом образует собственно программу на Прологе. С другой стороны, запрос можно рассматривать как директиву вызова Пролог-программы для выполнения. Синтаксис запроса выглядит как правило без заголовка: <запрос> ::= ?- <список подцелей> . ! <список подцелей> ?. Примеры запросов: 1) Tom дед Nick ?. Tom дед _X ?. 2) ELEMENT (_X, [1,3,5,7])?. или ?-ELEMENT (_X, [1,3,5,7]). 3) 100 > 10 ?. 4) ?-city(London, _where). Сложные термы, образующие факты или являющиеся подцелями правил или запросов, обычно называют предикатами. Совокупность правил, заголовки которых имеют одинаковые функторы и одинаковое количество элементов, называется процедурой, а совокупность фактов с такими же свойствами - отношением. 3. Семантика языка 3.1. Операция унификации 3.1.1. Связывание переменных Переменные, которым не присвоено значение, называются "свободными" переменными, процесс присваивания свободной переменной нового значения называется "связыванием" переменной с некоторым значением. После того, как переменная связана с некоторым значением, эта связь уже не может быть разорвана или изменена произвольным образом. Исключением является операция бектрекинга, при которой разрываются сразу все связи переменных, сделанные после некоторого момента времени. Две или более свободных переменных могут быть связаны друг с другом. Такие переменные называются "взаимосвязанными" свободными переменными. Взаимосвязанные переменные продолжают оставаться свободными, но связывание одной из них с некоторым значением означает автоматическое связывание с этим же значением всех переменных, взаимосвязанных с нею. Заметим, что, хотя связь переменных со значением и не может быть произвольно разорвана, но само значение может частично измениться уже после связывания с ним переменной, если в состав этого значения входят другие свободные переменные. Например, если переменная _W была связана со структурой str(_Q,_R,_S), где в момент связывания переменные _Q,_R,_S были свободными, то после связывания переменной _Q со значением 2, _R со значением [], а _S - со значением Москва, окажется, что переменная _W теперь связана со структурой str(2,[],Москва). Примечание 1: Не допускается связывание переменной со структурой, в которую входит сама эта переменная. Например, если есть переменная _Z и структура str(_X,_Y,_Z), связать _Z с ней нельзя, так как такое связывание породит структуру бесконечного объема str(_X,_Y,str(_X,_Y,str(_X,_Y,str(_X,_Y,str(_X,_Y,... Заметим, что такое ошибочное связывание может быть и косвенным, например, если _X связана со структурой str1(_Y,_Z), а _Z впоследствии связывается со структурой str2(_X). Примечание 2: Заметим, что "областью определения" имени переменной является только один клоз. Переменные с одним и тем же именем, но встречающиеся в различных клозах, являются различными. 3.1.2. Определение унификации Операция унификации выполняется над двумя операндами. Операндом может быть значение (число, символьная константа или структура) или свободная переменная. Если в состав операнда входит связанная переменная, то вместо нее в операции участвует то значение, с которым она связана. Результатом операции является логическое значение "истина" или "ложь". Первое означает, что объекты унифицируемы, второе - что нет. Если в состав операндов не входят свободные переменные, то унифицируемость двух объектов означает их полное тождество, если в состав операндов входят свободные переменные, то унифицируемость означает, что существует такой набор значений (такая подстановка), что после связывания с ними свободных переменных объекты станут тождественными. В этом случае побочным результатом унификации являются эти связывания. Если результат операции унификации "ложь", то никаких побочных эффектов она не имеет. Определение операции унификации следующее: 1) Целое число унифицируется только с равным ему целым числом. 2) Число с плавающей точкой связывается только с равным ему числом с плавающей точкой. (Заметим, что поскольку числа с плавающей точкой представляются в памяти машины приближенно, унификация кажущихся равными двух чисел может не быть успешной.) 3) Символьная константа унифицируется только с равной ей символьной константой (с учетом отбрасывания символа "#" в начале константы - см. раздел 2.2.). 4) Свободная переменная унифицируется с любым значением, при этом она связывается с ним. 5) Свободная переменная унифицируется с другой свободной переменной, при этом они становятся взаимосвязанными. 6) Структура унифицируется только со структурой, имеющей тот же функтор и то же количество элементов, при условии, что все элементы этих структур попарно унифицируемы. Попытка унификации элементов производится строго в порядке их следования в структурах. 7) Унификация выражений, списков и строк производится по правилам унификации структур, так как эти виды термов не что иное, как структуры. Рассмотрим действие операции унификации на примерах (в скобках указан побочный эффект унификации или причина неунифицируемости, знаком ':=' обозначается связь переменной со значением, все переменные полагаются свободными). 7 унифицируется с 7 7 не унифицируется с 7.0 7 не унифицируется с Москва 7 не унифицируется с 10 Москва унифицируется с Москва Лондон не унифицируется с Москва _Х унифицируется с Москва ( _Х:=Москва ) _Х унифицируется с _Y (_X,_Y становятся взаимосвязанными) str(1,2,3) унифицируется с str(1,2,3) str(1,2,3) не унифицируется с str(1,2) STR1(1,2) не унифицируется с STR2(1,2) [1,2,3] унифицируется с [1,2,3] "МОСКВА" унифицируется с "МОСКВА" стр(1,2,3) унифицируется с стр(_X,_Y,_Z) (_X:=1, _Y:=2, _Z:=3) [_X,2] унифицируется с [1,_Y] (_X:=1,_Y:=2) [1,2] не унифицируется с [_X,_X] (после унификации _Х с 1 это значение не унифицируется со значением 2) [1,2,3,4] унифицируется с [_X|_Y] (_X:=1, _Y:=[2,3,4]) [1,2,3,4] унифицируется с [1,2|_Y] ( _Y:=[3,4] ) [1,2,3] унифицируется с .(1,.(2,.(3,[]))) [1,2,3] не унифицируется с [1,[2,3]] _А+_В унифицируется с 2+3 (_А:=2, _В:=3) 2+3 не унифицируется с 3+2 (сравниваются не значения выражений, а представляющие их структуры '+'(2,3) и '+'(3,2), которые различаются ) 3.2. Логический вывод 3.2.1. Определение алгоритма логического вывода Выполнение программы начинается тогда, когда на вход системы попадает запрос, который состоит из списка подцелей. Алгоритм выполнения списка подцелей следующий: 0) Все входящие в запрос переменные считаются свободными. 1) Берется первая подцель в списке, она называется текущей подцелью. 2) Подцель всегда является структурой. Если функтор этой структуры входит во множество так называемых встроенных предикатов (т.е. предикатов, заранее реализованных в системе), то выполняется соответствующая подпрограмма интерпретатора; результатом ее выполнения является значение "истина" или "ложь" и, возможно, некоторое множество связываний переменных со значениями. Результат ее выполнения анализируется в п.4 алгоритма. 3) Если подцель не является вызовом встроенного предиката, то просматриваются все клозы программы и ищется такой факт, который унифицируется с подцелью, или такое правило, заголовок которого унифицируется с подцелью. Если такого клоза не нашлось, то результат вычисления подцели - "ложь". Если таких клозов несколько, то текущая подцель отмечается как "точка отката" и берется первый из них. Если это факт, то результат вычисления подцели - "истина". Результат вычисления подцели анализируется в пункте 4. Если найденный клоз есть правило, то берется список его подцелей и присоединяется слева к имеющемуся списку подцелей. Текущая подцель называется теперь вызывающей подцелью, а список подцелей правила вычисляется по данному алгоритму, начиная с п.1 (при условии, что некоторые из переменных правила могли быть связаны в процессе унификации заголовка правила и вызывающей подцели). 4) Если результат вычисления текущей подцели есть "истина", то берется следующая подцель в списке и вычисляется, начиная с п.2. Если список подцелей исчерпан, то это означает, что применение данного правила к вызывающей подцели дало результат "истина", в этом случае берется подцель, следующая за вызывающей, и вычисляется, начиная с п.2. Если вызывающей подцели нет, то есть завершено вычисление списка подцелей запроса - это означает, что запрос успешно выполнен. При этом автоматически выводятся значения всех переменных запроса. Если система работает в режиме поиска всех решений, то после вывода значений переменных дальнейшие вычисления производятся так, как если бы результатом вычисления текущей подцели была "ложь". Это позволяет найти другие решения запроса, если они существуют. 5) Если результат вычисления текущей подцели "ложь", то выполняется "откат" (BACKTRACKING), т.е. состояние всего процесса вычисления возвращается в тот вид, который он имел в момент отметки последней точки отката. Если ни одной такой точки нет, то это означает, что запрос не может быть успешно выполнен, вычисления прекращаются с результатом "ложь". При возврате в последнюю отмеченную точку отката разрываются связи всех переменных, установленные после отметки точки отката, и делается попытка найти другое решение текущей подцели. Выполняется п.3, но не с начала, а с клоза, следующего за ранее найденным. Замечание. Поскольку в Прологе не существует понятия функции в том смысле, в каком оно существует в традиционных языках программирования, а все элементы данных могут выступать лишь как аргументы предикатов, если нужно, чтобы процедура на Прологе вырабатывала какое-то значение, в числе аргументов этой процедуры должен быть один аргумент, в которое в результате выполнения этой процедуры будет записано это значение. В запросе этот аргумент должен быть свободной переменной. (На самом деле в системе PROLOGUS используются математические функции, входящие в арифметические выражения, и функция оценки арифметического выражения ВЫЧ, однако они не являются элементами чистого языка Пролог и имеют весьма ограниченное применение -- см. раздел 4.1) Пример: Пусть нам нужно написать процедуру, которая вычисляет факториал числа 10. Мы не можем записать обращение к ней так: ?- _X = факт(10). а должны написать так: ?- факт(10,_X). результат будет записываться в аргумент _X. 3.2.2. Примеры Пример 1. Рассмотрим, как в Пролог-программах осуществляется бектрекинг. Пусть база данных Пролога содержит следующие факты (likes - любит): likes (John, jazz) likes (John, Geraldine) likes (John, escargot) likes (Geraldine, John) likes (Geraldine, escargot) Наша задача: определить, что John и Geraldine любят оба. Таким образом, запрос записывается так: ?- likes (John, _X), likes (Geraldine, _X). Делается попытка согласовать первую цель путем перемещения по базе следующим образом : 1. Находится факт, что John любит jazz. 2. _X конкретизируется значением jazz. 3. Делается попытка согласовать вторую цель путем проверки, любит ли Geraldine jazz. 4. Согласование является неуспешным, поскольку нет факта likes (Geraldine, jass). 5. Делается бектрекинг для повторного согласования цели likes (John, _X), при этом конкретизация _X значением jass аннулируется. 6. Находится факт, что John любит Geraldine. 7. _X конкретизируется значением Geraldine. 8. Делается попытка согласовать вторую цель путем проверки, любит ли Geraldine Geraldine. 9. Согласование является неуспешным, поскольку нет факта likes (Geraldine, Geraldine). 10. Делается бектрекинг для повторного согласования цели likes (John, _X), при этом конкретизация _X значением Geraldine аннулируется. 11. Находится факт, что John любит escargot. 12. Находится факт, что Geraldine любит escargot. 13. Выполнение программы заканчивается успехом, при этом _X конкретизируется значением escargot. В следующем разделе при рассмотрении встроенных предикатов мы увидим, что многие из них, например, read (чтение терма), get (чтение символа), не осуществляют бектрекинга. Пример 2. Рассмотрим следующий пример: будем представлять информацию о родственных отношениях некоторой группы лиц (для простоты будем рассматривать только лиц мужского пола). Эти отношения будем представлять фактами вида отец(_X,_Y), означающими, что _Х является отцом _Y. Это отношение может, например, иметь вид: отец(Иван,Петр). отец(Иван,Николай). отец(Иван,Сергей). отец(Петр,Александр). отец(Петр,Георгий). отец(Николай,Михаил). и т.д. Рассмотрим возможные виды запросов: 1) ?-отец(Петр,Георгий). Ответ - ДА, т.к. подцель унифицируется с одним из фактов. 2) ?-отец(_X,Георгий). % (кто отец Георгия?) Ответ: _Х=Петр Существует только один факт, унифицируемый с подцелью, при этой унификации переменная _Х получает значение 'Петр'. 3) ?-отец(Иван,_Х). % (чей отец Иван?) Ответ: _Х=Петр _Х=Николай _Х=Сергей После печати первого найденного решения выполняется откат и поиск другого решения (предполагается, что Пролог работает в режиме поиска всех решений). 4) ?-отец(_Х,Николай), отец(_Х,Георгий). (Является ли отец Николая одновременно и отцом Георгия?) Первая подцель унифицируется с фактом отец(Иван,Николай), _Х связывается со значением 'Иван', следующая подцель теперь имеет вид отец(Иван,Георгий), унифицируемых с нею фактов не находится, поэтому ответ системы - НЕТ. Можно было бы определить и другие факты, отражающие другие виды отношений: 'сын', 'брат', 'дед' и т.п. однако отметим, что одного отношения 'отец' достаточно, чтобы определить все возможные отношения в виде правил, а не фактов: сын(_X,_Y):-отец(_Y,_X), брат(_X,_Y):-отец(_Z,_X),отец(_Z,_Y). дед(_X,_Y):-отец(_X,_Z),отец(_Z,_Y). и т.п. Теперь можно формулировать более сложные запросы: 5) ?-дед(Иван,Михаил). Ответ: ДА 6) ?-внук(_Х,Иван)?. % (Кто внуки Ивана?) Ответ: _Х=Александр _Х=Георгий _Х=Михаил 7) ?-брат(_Х,Георгий). % (Кто братья Георгия?) Ответ: _Х=Александр _Х=Георгий Последний курьезный ответ проистекает из упрощенной формулировки правила "брат" - в нашем примере это правило предполагает, что любой человек является братом самому себе. 3.2.3. Рекурсия Одним из мощных средств управления ходом выполнения программы на Прологе является рекурсия. Рекурсивная процедура содержит в своем определении сама себя, т.е. предикат, стоящий в левой части правила, входит в число подцелей в правой части правила. Таким образом, этот предикат включает подцели, которые могут быть успешно согласованы только с помощью данного предиката. Пример 1: _X является элементом списка либо если _X является головой списка, либо если _X является элементом хвоста списка. элемент (_X, [ _X|_ ]). элемент (_X, [ _ |_Y ]) :- элемент (_X, _Y). Для того чтобы рекурсивная процедура не зацикливалась, она должна содержать нерекурсивный факт или правило, с головой которого происходит сопоставление при выполнении некоторого граничного условия (так называемое "граничное правило").. При попытке согласования рекурсивной цели возможен один из трех вариантов: - согласуется рекурсивное правило - согласуется граничное правило - согласование неуспешно. Рекурсия заканчивается, когда согласуется граничное правило или когда и граничное, и рекурсивное правила - не согласуются. Пример 2. Предикат предок: предок (_X, _Y):- родитель (_X, _Y). предок (_X, _Y):- родитель (_X, _Z), предок (_Z, _Y). Если первое правило успешно, предикат успешен. Рекурсивное второе правило может вызываться несколько раз. 4. Встроенные предикаты Встроенные предикаты В Прологе аналогичны библиотечным процедурам или функциям в традиционных языках программирования. Как отмечалось выше, если при выполнении алгоритма логического вывода функтор текущей подцели входит во множество встроенных предикатов, поиск в базе данных Пролога не осуществляется, а вызывается зафиксированная в интерпретаторе подпрограмма, соответствующая данному встроенному предикату и вырабатывающая значение "истина" или "ложь". В системе PROLOGUS каждый встроенный предикат имеет два варианта написания -- с помощью русских прописных букв и с помощью латинских строчных букв -- которые являются полностью эквивалентными. Замечание 1. При описании каждого встроенного предиката указывается, какого типа могут быть его аргументы. В случае, если указываемый тип - не свободная переменная, всегда допускается, чтобы аргументом была также переменная, на момент выполнения предиката связанная со значением указанного типа. Например, если мы пишем "аргумент - число", то имеется в виду, что аргументом может быть либо число, либо переменная, связанная с числом. Замечание 2. В отличие от произвольных символьных констант в начальной позиции имен встроенных предикатов запрещается использовать сивол '#'. 4.1. Арифметические предикаты Эта группа встроенных предикатов позволяет выполнять простейшие вычисления над целыми числами и числами с плавающей точкой. К данной группе встроенных предикатов относятся: ЕСТЬ is ВЫЧ eval СЛОЖ add УМНОЖ mult ЛИНЗАВ lindep КВАДЗАВ sqrdep Последние четыре предиката выполняются только над целыми аргументами. Аргументами первых двух предикатов являются арифметические выражения, построенные по правилам, определенным в подразделе 2.4.2. Помимо арифметических операций в выражениях могут использоваться вещественные функции. В системе PROLOGUS допускаются следующие функции: exp - экспонента ln - натуральный логарифм sqrt - квадратный корень sin - синус cos - косинус arctan - арктангенс trunc - отбрасывание дробной части round - округление Каждая функция имеет один аргумент, который должен быть вещественным или целым числом. Результат - целое число для функций trunc и round и вещественное для всех остальных. Эти функции могут использоваться только в арифметических выражениях и не могут выступать как подцели. 4.1.1. Предикат _X ЕСТЬ _Y (_X is _Y) _X - свободная переменная или число _Y - выражение, в которое входят только операции + - * / mod, и числа или переменные, связанные с числом. В случае вещественных чисел в выражение могут также входить вещественные функции. Результат: если _Х - переменная - "истина"; если _Х - число, то результат есть результат сравнения с ним значения выражения в правой части. Побочные эффекты: связывания, сделанные при унификации Ошибки: _Х не свободная переменная и не число _Y не есть выражение заданного вида деление на нуль недопустимые аргументы вещественной функции (например, квадратный корень из отрицательного числа, отрицательный аргумент логарифма и т.п.) Примеры: 1) ?- _X is 2+3, 20 is _X*4. Ответ: Да 2) ?- 10 ЕСТЬ sqrt(100). Ответ: Нет (т.к. правая и левая части относятся к разным типам данных) 3) ?- _X is _Y mod 10 Ошибка (т.к. переменная _Y неконкретизирована). 4.1.2. Функция ВЫЧ(_V) ( eval(_V) ) _V - выражение, определенное по тем же правилам, что и в случае предиката ЕСТЬ Функция ВЫЧ (eval) не является предикатом, она не может выступать в роли самостоятельной подцели, но может входить в состав других подцелей. В этом случае перед выполнением подцели выражение, являющееся аргументом, вычисляется (все входящие в него переменные должны быть уже определены) и полученное значение используется так, как если бы это значение ранее было присвоено переменной, а в подцели была бы указана эта переменная. Ошибки: Аргумент функции не является правильным выражением. В выражении используются неопределенные переменные. Деление на нуль Недопустимые аргументы вещественной функции. Функция использована в факте или заголовке правила. Примеры: 1) Рассмотрим применение ВЫЧ в процедуре определения факториала: fact(0,1). fact(_n,_x) :- fact(ВЫЧ(_n-1),_y), _x is _y*_n. 2) ?- eval(ln(100)*exp(-10)). Ошибка, т.к. eval не может выступать как самостоятельная подцель. 4.1.3. Предикат СЛОЖ(_A,_B,_C) ( add(_A,_B,_C) ) _A, _B, _C - целые числа или свободные переменные. Результат зависит от сочетания типов аргументов. 1) _A, _B - числа, _C - число или свободная переменная. Вычисляется сума _A и _B и результат унифицируется с _C. Результат предиката есть результат этой унификации. 2) _A - свободная переменная, _B, _C - числа. _A унифицируется с разностью _C и _B. 3) _B - свободная переменная, _A, _C - числа. _B унифицируется с разностью _C и _A. Все остальные сочетания аргументов рассматриваются как ошибка. Примеры: 1) ?- СЛОЖ(1,1,_X). Ответ: 2 2) ?- СЛОЖ(10,12,100). Ответ: Нет 3) ?- add(_X,15,20). Ответ: 5 4) ?- add(_X,_Y,200). Ошибка (т.к. не могут два аргумента быть переменными). 4.1.4. Предикат УМНОЖ(_A,_B,_C) ( mult(_A,_B,_C) ) _A, _B, _C - целые числа или свободные переменные. Результат зависит от сочетания типов аргументов. 1) _A, _B - числа, _C - число или свободная переменная. Вычисляется произведение _A и _B и результат унифицируется с _C. Результат предиката есть результат этой унификации. 2) _A - свободная переменная, _B, _C - числа. Если _C нацело делится на _B, _A унифицируется с частным от этого деления, в противном случае результат предиката - "ложь". 3) _B - свободная переменная, _A, _C - числа. Если _C нацело делится на _B, _A унифицируется с частным от этого деления, в противном случае результат предиката - "ложь". Все остальные сочетания аргументов рассматриваются как ошибка. Примеры: 1) ?- УМНОЖ(2,5,10). Ответ: Да 2) ?- mult(3,_X,20). Ответ: Нет (т.к. 20 не делится на 3). 3) ?- УМНОЖ(abcde,_Y). Ошибка (т.к. аргумент не может быть символьной константой). 4.1.5. Предикат ЛИНЗАВ(_A,_B,_C,_D) ( lindep(_A,_B,_C,_D) ) Все аргументы - целые числа или свободные переменные. Результат зависит от сочетания типов аргументов. 1) _D - число или свободная переменная, остальные аргументы - числа. Вычисляется выражения _A*_B+_C и его результат унифицируется с _D. Результат предиката есть результат этой унификации. 2) _A - переменная, остальные аргументы - числа. Если (_D-_C) mod _B = 0 , результат "истина" и _A унифицируется с (_D-_C)/_B, в противном случае результат - "ложь". 3) _B - переменная, остальные аргументы - числа. Если (_D-_C) mod _A = 0 , результат "истина" и _B унифицируется с (_D-_C)/_A, в противном случае результат - "ложь". 3) _A, _C - переменные, _B, _D - числа. Выполняется деление _D на _B с остатком. Частное присваивается _A, остаток присваивается _C. 4) _B, _C - переменные, _A, _D - числа. Выполняется деление _D на _A с остатком. Частное присваивается _B, остаток присваивается _C. Все остальные сочетания аргументов рассматриваются как ошибка. Примеры: 1) ?- ЛИНЗАВ(2,10,5,_x). Ответ: _x = 25 2) ?- ЛИНЗАВ(_X,3,10,19). Ответ: _X = 3 3) ?- lindep(5,_x,7,20). Ответ: Нет (т.к. 20-7 не делится на 5) 4) ?- ЛИНЗАВ(_X,10,_Y,56). Ответ: _X = 5 _Y = 6 5) ?- lindep(_x,_y,10,20). Ошибка, т.к. первый и второй аргументы не могут быть оба переменные 4.1.6. Предикат КВАДЗАВ(_A,_B) ( sqrdep(_A,_B) ) _A, _B - целые числа или свободные переменные. Результат зависит от сочетания типов аргументов. 1) _B - число или свободная переменная, _A - число. Вычисляется значение _A*_A и результат унифицируется с _B. Результат предиката есть результат этой унификации. 2) _A - свободная переменная, _B - число. Если из _B можно извлечь целый квадратный корень, результат "истина" и _A унифицируется со значением этого корня, в противном случае результат - "ложь". Все остальные сочетания аргументов рассматриваются как ошибка. Примеры: 1) ?- КВАДЗАВ(5,25). Ответ: Да 2) ?- sqrdep(_X,18). Ответ: Нет (т.к. из 18 нельзя извлечь целый квадратный корень) 4.2. Предикаты сравнения К данной группе встроенных предикатов относятся: = \= предикаты сравнения значений выражений предикаты сравнения термов == и \== 4.2.1. Предикат: _X = _Y. _Х и _У - произвольные термы или свободные переменные. Результат: результат предиката есть результат выполнения над объектами _Х и _У операции унификации. Побочные эффекты: если результат унификации "истина", то побочными эффектами являются связывания переменных, сделанные при унификации. Если результат - "ложь", то побочных эффектов нет. Замечание: Предикат "=" не следует путать с предикатом ЕСТЬ (is). Разница между этими предикатами состоит в следующем: 1) Предикат "=" можно применять к произвольным термам, тогда как на аргументы предиката ЕСТЬ накладываются ограничения: левая часть может быть только свободной переменной или числом, правая часть - арифметическим выражением. 2) Эти два предиката по-разному осуществляют унификацию. Если ввести следующий запрос: ?- _X is 5 + 3. ответ будет _X = 8 т.к. предикат is связывает переменную с результатом вычисления выражения. Если же ввести запрос ?- _X = 5 + 3 ответ будет _X = '+'(5,3) т.к. предикат "=" при унификации трактует правую часть как структуру. По этой же причине ответ на запрос ?- 10 is 5 + 5. будет "Да", а на запрос ?- 10 = 5 + 5. будет "Нет". 4.2.2. Предикат: _X \= _Y _X и _Y - произвольные термы или свободные переменные. Результат: выполняется операция унификации над _Х и _Y, если она прошла успешно, то результат "ложь", иначе - "истина". Побочные эффекты: нет. Примеры: 1) ?- abcd \= f(1<_X). Ответ: Да (т.к. приведенные здесь термы не унифицируются) 2) ?- _X \= 10. Ответ: Нет (т.к. переменная унифицируется с любым термом). 4.2.3. Предикаты сравнения значений выражений _Х<_Y, _Х>_Y, _Х=<_Y, _Х>=_Y, _X=:=_Y, _X=\=_Y _X, _Y - арифметические выражения или переменные, связанные с арифметическими выражениями Результат: результат сравнения двух числовых значений. Операция =:= обозначает сравнение на равенство двух числовых значений. Операция =\= - сравнение на неравенство. Побочные эффекты: нет. Ошибки: аргументы не являются арифметическими выражениями Примеры: 1) ?- 10 + 4 =:= 20. Ответ: Нет 2) ?- _X is 10*2, _X > 14. Ответ: Да 4.2.4. Предикаты сравнения термов _X==_Y, _X\==_Y _Х, _Y - произвольные термы Эти предикаты выполняет сравнение двух термов. В отличие от предикатов '=' и '\=', никаких попыток унификации термов здесь не производится. В отличие от предикатов сравнения значений выражений эти предикаты вычисления арифметических операций не производят. Предикат _X==_Y имеет значение "истина" в том и только в том случае, когда два терма _X и _Y полностью совпадают. Если _X и _Y - переменные, то _X==_Y - "истина" тогда и только тогда, когда это взаимосвязанные переменные. Примеры: 1) ?- '+++' == _X. Ответ: Нет 2) ?- _x == _y. Ответ: Нет 3) ?- _x = _y, _x == _y. Ответ: Да (т.к. после выполнения первой подцели переменные _x и _y становятся взаимосвязанными). 4.3. Предикаты определения типа К данной группе встроенных предикатов относятся: ПЕРЕМ var НЕПЕРЕМ nonvar ЦЕЛ integer ВЕЩЕСТ real АТОМ atom АТОМАР atomic У всех предикатов данной группы операнды могут быть любыми, побочных эффектов нет, ошибок нет. 4.3.1. Предикат ПЕРЕМ(_A) ( var()A) ) Результат: "истина", если _А - свободная переменная. Примеры: 1) ?- ПЕРЕМ(f(a,b)). Ответ: Нет 2) ?- var(_x). Ответ: Да 4.3.2. Предикат НЕПЕРЕМ(_A) ( nonvar(A) ) Результат: "истина", если _А не является свободной переменной. Пример: ?- _X = 10, nonvar(_X). Ответ: Да 4.3.3. Предикат ЦЕЛ(_A) ( integer(_A) ) Результат: "истина", если _А - число. Примеры: 1) ?- _X = 1000, integer(_X). Ответ: Да. 2) ?- _X ЕСТЬ 10 + 10, ЦЕЛ(_X). Ответ: Да 3) ?- _X = 10 + 10, ЦЕЛ(_X). Ответ: Нет (т.к. переменная _X связана не с числом 20, а со структурой '+'(10,10). 4.3.4. Предикат ВЕЩЕСТ(_A) ( real(_A) ) Результат: "истина", если _А - число с плавающей точкой. Примеры: 1) ?- _X is sin(10), real(_X). Ответ: Да 2) ?- ВЕЩЕСТ(100). Ответ: Нет 4.3.5. Предикат АТОМ(_A) ( atom(_A) ) Результат: "истина", если _А - символьная константа. Примеры: 1) ?- АТОМ(abcde). Ответ: Да 2) ?- atom("abcde"). Ответ: Нет (т.к. аргумент - не символьная константа, а строка, т.е. список кодов букв). 4.3.6. Предикат АТОМАР(_A) ( atomic(_A) ) Результат: "истина", если _А - число или символьная константа. Примеры: 1) ?- atomic('1234456'). Ответ: Да 2) ?- atomic(eval(100 mod 17)). Ответ: Да 3) ?- АТОМАР(100 mod 17). Ответ: Нет 4.4. Предикаты преобразования К данной группе встроенных предикатов относятся: =.. ФУНКТОР functor АРГ arg СТРОКАСПИС name СТРОКАЦЕЛ textint 4.4.1. Предикат _S =.. _L _S - свободная переменная или структура _L - свободная переменная или список Результат зависит от сочетания типов аргументов. 1) Если _S - структура, то она преобразуется в список, состоящий из символьной константы, равной функтору структуры, и из элементов структуры. Полученный список унифицируется с _L. Если _L - свободная переменная, она связывается с полученным списком. Если _L - список, то он унифицируется с полученным списком. Результат предиката есть результат операции унификации . 2) Если _S - свободная переменная, то список _L преобразуется в структуру, имеющую функтор равный первому элементу списка, который должен быть символьной константой. В качестве элементов структуры берутся элементы списка, начиная со второго. _S связывается с полученным списком, результат - "истина". Побочные эффекты: связывания, сделанные при унификации. Ошибки: _S - не структура и не свободная переменная _L - не список и не свободная переменная _S и _L одновременно свободные переменные Первый элемент списка _L - не символьная константа Примеры: 1) Следующий запрос преобразует структуру в список: ?- страна(Франция,столица(Париж)) =.. _X Ответ: _X = [страна, Франция, столица(Париж)] 2) Следующий запрос осуществляет обратное преобразование: ?- _X =.. [страна, Франция, столица(Париж)]. 3) Пример применения данного предиката. Пусть требуется добавить аргументы к структуре. Для этого мы определим предикат доб_арг, который будет обращаться к предикату =.. для преобразования структуры в список, затем с помощью предиката "сцепление" добавлять элементы к списку, а после этого выполнять обратное преобразование: доб_арг(_стр1,_АргСпис,_стр2) :- _стр1 =.. _спис1, сцепление(_спис1,_АргСпис,_спис2), _стр2 =.. _спис2. сцепление([],_L,_L). сцепление([_H|_T],_L,[_H|_T1] :- сцепление(_T,_L,_T1). 4.4.2. Предикат ФУНКТОР(_S,_F,_N) ( functor(_S,_F,_N) ) _S - атом, структура или свободная переменная _F - атом или свободная переменная _N - число или свободная переменная Результат зависит от сочетания типов аргументов. 1) _S - символьная константа или структура. Подсчитывается количество элементов структуры _S (если это символьная константа, то оно полагается равным нулю). Это число унифицируется с _N, функтор структуры _S (или символьная константа) унифицируется с _F. Результат "истина", если обе унификации прошли успешно. 2) _S - свободная переменная. Создается новая структура с функтором _F и количеством элементов _N (_F и _N должны быть определены). Все элементы структуры - свободные переменные, переменная _S связывается с этой структурой. Побочные эффекты: связывания, сделанные при унификациях. Ошибки: _S - не символьная константа, не структура и не переменная _F - не символьная константа и не переменная _N - не число и не переменная Примеры: 1) ?- ФУНКТОР(книга(Толстой,Воскресенье),_F,_N). Ответ: _F = Толстой _N = 2 2) ?- functor(_S,newstruct,3). Ответ: _S = newstruct(_1,_2,_3) 4.4.3. Предикат АРГ(_N,_S,_V) ( arg(_N,_S,_V) ) _N - число или свободная переменная _S - структура _V - терм или свободная переменная Результат зависит от сочетания типов аргументов. 1) _N - число, _V - переменная или терм. _V унифицируется с _N-ным элементом структуры _S. Результат предиката есть результат этой унификации. 2) _N - свободная переменная, _V - терм. Ищется такой элемент структуры, который унифицируется с _V. Если такой элемент найден, то его номер присваивается _N и результат "истина", иначе результат "ложь". 3) _N - свободная переменная, _V - свободная переменная. _V унифицируется с первым элементом структуры, _N унифицируется с 1, результат "истина", предикат отмечается как точка возврата, если при последующем выполнении программы будет сделан возврат к этому предикату, то результатом будет связывание _V со вторым элементом структуры, а _N - с 2 и т.д. Эта форма предиката может быть использована для перебора всех элементов структуры. Побочные эффекты: результаты связываний Ошибки: _S - не структура _N <= 0 _N превышает количество элементов структуры или списка (для случая 1) Примеры: 1) ?- arg(1,книга(Толстой,Воскресенье),_X). Ответ: _X = Толстой 2) ?- arg(_N,str(abc,100,_X,f(1,2)),f(_y,_z)). Ответ: _N = 4 _y = 1 _z = 2 4.4.4. Предикат СТРОКАСПИС(_A,_L) ( name(_A,_L) ) Результат зависит от сочетания типов аргументов. 1) _A - символьная константа или структура _L - свободная переменная или список Результат: символьная константа _А (или функтор структуры _А) преобразуется в строку, которая унифицируется с параметром _L. Результат предиката есть результат этой унификации. 2) _A - свободная переменная _L - список чисел от 0 до 256. Результат: переменная _A унифицируется с символьной константой, коды символов которой являются элементами списка _L. Ошибки: не выполнено ни одно из условий (1) или (2). Примеры: 1) ?- СТРОКАСПИС(abcde,_X). Ответ: _X = [97,98,99,100,101] 2) ?- name(_X,[49,50,51,52,53,54]). Ответ: _X = '123456' 3) ?- name(abcdef,"abbcc'). Ответ: Нет 4.4.5. Предикат СТРОКАЦЕЛ(_A,_N) ( textint(_A,_N) ) Результат определяется по разному, в зависимости от сочетаний параметров. 1) _A - символьная константа или структура _N - свободная переменная или целое число Результат: символьная константа _А (или функтор структуры _А) преобразуется в целое число, которое унифицируется с параметром _N. Результат предиката есть результат этой унификации. 2) _A - свободная переменная _N - целое число Результат: переменная _A унифицируется с символьной константой, символы которой являются цифрами числа _N. Ошибки: не выполнено ни одно из условий (1) или (2). Примеры: 1) ?- СТРОКАЦЕЛ('344',_X). Ответ: _X = 344 2) ?- textint(_X,32767). Ответ: _X = '32767' 4.5. Предикаты ввода-вывода термов Эта группа предикатов служит для ввода-вывода результатов во время выполнения программы. Вывод производится на терминал (может быть предназначен на печать или на дисковый файл предикатом ЗАПИСЬ_В или tell). Ввод производится с терминала (может быть переназначен на ввод из дискового файла предикатом ЧТЕНИЕ_ИЗ или see). К данной группе встроенных предикатов относятся следующие: ВЫВОД write ВЫВКОД put ПРОБЕЛЫ tab ПС nl ОЧИСТИТЬ clrscr КУРСОР cursor ЗАПИСЬ_В tell ЧТЕНИЕ_ИЗ see ВВОДЦЕЛ read ВВОДСИМВ readatom ВВОДКОД0 get0 ВВОДКОД get ЗАПИСЬ_КОН told ЧТЕНИЕ_КОН seen Все предикаты, выполняющие вывод, повторно не согласуются. Это означает, что в случае бектрекинга к точке, находящейся до выполнения этого предиката, их действие не аннулируется. Результат этих предикатов всегда "истина". Предикаты же, выполняющие ввод, могут осуществлять связывание переменной. При бектрекинге эти связывания аннулируются. Однако повторного согласования этих предикатов в том смысле, в котором это происходит с другими предикатами, здесь происходить не может (т. к. введенная информация не может быть "возвращена" на терминал или в файл). Если в результате бектрекинга управление повторно попадает на предикат ввода, вводится уже новая информация и унификация производится с ней. 4.5.1. Предикат ВЫВОД(_X) ( write(_X) ) _X - произвольный терм. Результат: вывод терма _Х на терминал. Терм выводится, начиная с очередного символа выводимой строки, при этом все пробелы, которые, возможно, содержались в исходном представлении терма, опускаются. Вместо переменных выводятся значения, с которыми они связаны. Вместо свободных переменных выводится символ #. Если термом является выражение, то оно выводится в виде структуры. Примеры: 1) ?- ВЫВОД(abcdefg). Результат: Выведется строка abcdefg 2) ?- write(10 + 5). Результат: Выведется структура '+'(10,5) 3) ?- write(eval(10+5)). Результат: Выведется число 15 4) ?- ВЫВОД(f(1,_X,ggg)). Результат: Выведется структура f(1,#,ggg) 4.5.2. Предикат ВЫВКОД(_C) ( put(_C) ) _С - число Результат: Выводится символ, код которого равен _С, если _С>255, то выводится символ, код которого равен значению _С по модулю 256. Ошибка: _С - не число. Пример: ?- put(65). Результат: Выведется буква A 4.5.3. Предикат ПРОБЕЛЫ(_N) ( tab(_N) ) _N - число Результат: вывод на терминал _N пробелов. Ошибка: _N - не число 4.5.4. Предикат ПС ( nl ) Результат: смена строки на терминале. 4.5.5. Предикат ОЧИСТИТЬ ( clrscr ) Результат: очистка экрана терминала. 4.5.6. Предикат КУРСОР(_M,_N) ( cursor(_M,_N) ) _M,_N - числа (1<=M<=24, 1<=N<=80 ) Результат: перевод курсора в _N-ю позицию _M-й строки экрана. 4.5.7. Предикат ВВОДЦЕЛ(_N) ( read(_N) ) _N - свободная переменная или целое число Результат: Делается запрос на ввод с терминала. С терминала вводится целое число, заканчивающееся нажатием клавиши Enter. Введенное число унифицируется с параметром _N. Результат этой унификации есть результат предиката. Ошибки: _X не число и не свободная переменная. Примеры: 1) ?- ВВОДЦЕЛ(_X). Если после этого ввести с терминала число 100, результат будет: _X = 100 2) ?- read(10). Если после этого ввести с терминала число 999, результат будет: Нет 3) ?- read(abcde). Ошибка (т.к. аргумент не число и не свободная переменная) 4.5.8. Предикат ВВОДСИМВ(_A) ( readatom(_A) ) _А - символьная константа или свободная переменная. Выполняется так же, как ВВОДЦЕЛ. Введенная с терминала строка, заканчивающаяся символом смены строки, трактуется как символьная константа. Ошибки: _X не число и не символьная константа. Примеры: 1) ?- ВВОДЦЕЛ(_X). Если после этого ввести с терминала '123fdsa', результат будет: _X = 123fdsa 2) ?- readatom('10'). Если после этого ввести с терминала число 10, результат будет: Нет (т.к. число 10 - это не символьная константа символьной константой '10' не унифицируется). 3) ?- readatom(100). Ошибка (т.к. аргумент не и не свободная переменная) 4.5.9. Предикат ВВОДКОД0(_N) ( get0(_N) ) _N - свободная переменная или число. Результат: из входной строки считывается очередной символ и его код унифицируется с _N. Результат предиката есть результат этой унификации. Ошибки: _N не число и не свободная переменная. Примеры: 1) ?- ВВОДКОД(48). Если после этого вести с терминала символ '0', результат будет: Да 2) ?- ВВОДКОД(_x). Если после этого вести с терминала символ 'A', результат будет: _x = 65 4.5.10. Предикат ВВОДКОД(_N) get(_N) Предикат выполняется точно так же как и "get0", но предварительно пропускаются управляющие символы (с кодами меньшими, чем код пробела - 32). Пример: Программа rdb вводит с терминала последовательно символы (пропуская управляющие символы) и, как только встретит символ 'B', заканчивает ввод и выводит его код на терминал. rdb :- get(_X), _X = 66, write(_X). rdb :- rdb. (На самом деле эта программа должна выглядеть чуть сложнее, мы к ней вернемся в следующем разделе, когда буде рассматривать предикат отсечение). 4.5.11. Предикат ЗАПИСЬ_В(_F) ( tell(_F) ) _F - символьная константа. Результат: _F трактуется как имя файла на диске или имя логического устройства MS-DOS, и этот файл открывается на вывод. После этого все операции вывода на терминал, производимые при выполнении запросов (результаты предикатов вывода), будут переадресованы в этот файл. Назначение сохраняется до тех пор, пока не будет выполнен предикат told (или эквивалентный ему ЗАПИСЬ_КОН) или tell(con) (ЗАПИСЬ_В(con)). Если не указан путь, то файл открывается в текущей директории. Если не указано расширение, то по умолчания берется расширение .pro . Ошибки: _F - не символьная константа. 4.5.12 Предикат told (ЗАПИСЬ_КОН) Результат: закрывается ранее открытый выходной файл и вывод вновь назначается на терминал. Пример: ?- tell('outres.pro'),write(abcde),told,write(1000). В результате в файл outres.pro запишется последовательность символов abcde, а на терминал выведется число 1000. 4.5.13. Предикат ЧТЕНИЕ_ИЗ(_F) ( see(_F) ) _F - символьная константа. Результат: _F трактуется как имя файла на диске, и этот файл открывается на ввод. После этого все операции ввода c терминала, производимые при выполнении запросов (результаты предикатов ввода), будут переадресованы на ввод из этого файла. Назначение сохраняется до тех пор, пока не будет выполнен предикат seen (или эквивалентный ему ЧТЕНИЕ_КОН) или see(con) (ЧТЕНИЕ_ИЗ(con)). Если не указан путь, то файл открывается в текущей директории. Если не указано расширение, то по умолчания берется расширение .pro . Ошибки: _F - не символьная константа. 4.5.14 Предикат seen ( ЧТЕНИЕ_КОН ) Результат: закрывается ранее открытый выходной файл и вывод вновь назначается на терминал. Пример: ?- see('srcfile.pro'),read(_X),seen,readatom('rrrr'). Из файла srcfile.pro считывается число, с которым связывается переменная _X, а затем с терминала вводится строка, которая унифицируется с символьной константой 'rrrr'. Если унификация прошла успешно, результат: _X = < введенное из файла число > Если неуспешна, результат Нет 4.6. Предикаты управления логическим выводом Эта группа предикатов служит для целей управления процессом выполнения программы. К данной группе встроенных предикатов относятся: ВЫП call НЕ not ! (отсечение) cut ЛОЖЬ fail , ; ТРАС trace НЕТРАС notrace СЛЕД spy НЕСЛЕД nospy ВЫХОД quit 4.6.1. Предикат ВЫП(_T) ( call(_T) ) _T - структура или символьная константа (или переменная, связанная со структурой или с символьной константой). _T рассматривается как цель и выполняется. Результат предиката есть результат выполнения этой цели. Ошибки: _T - не символьная константа и не структура. Этим предикатом целесообразно пользоваться тогда, когда функтор подцели неизвестен в момент ввода программы в базу данных Пролога. В этом случае в качестве аргумента предиката ВЫП записывается переменная, которая в процессе выполнения программы до обращения к этому предикату связывается со структурой или с символьной константой Например: ?- _X = p(1,2,3),call(_X). Результат: будет выполняться цель p(1,2,3). 4.6.2. Предикат НЕ(_T) ( not(_T) ) _T - символьная константа или структура. Результат: терм _Т выполняется как подцель. Если результат выполнения подцели _Т - "истина", то результат предиката not(_T) - "ложь" и наоборот. Ошибки: _T - не символьная константа и не структура. Замечание: Этим предикатом нужно пользоваться с большой осторожностью. В частности, не рекомендуется применять его к целям, которые в момент его выполнения содержат свободные переменные. Это соображение можно проиллюстрировать следующим примером: Рассмотрим базу данных, описывающую, кто что любит, приведенную в разделе 3: likes (John, jazz). likes (John, Geraldine). likes (John, escargot). likes (Geraldine, John). likes (Geraldine, escargot). Нас теперь будет интересовать, кто что НЕ любит. Определим соответствующее правило с помощью предиката not: не_любит(_X,_Y) :- not(likes(_X,_Y). Если теперь ввести запрос ?- не_любит(Geraldine,jazz). ответ будет "Да", т.к. в базе нет факта likes(Geraldine,jazz). Поэтому результат согласования подцели likes(Geraldine,jazz) - ложь и,следовательно, результат нашего запроса - истина. Однако если ввести запрос ?- не_любит(_X,jazz). ответ будет не _X = Geraldine (как Вы ожидали), а "Нет", т. к. результат согласования подцели likes(_X,jazz) - истина (поскольку в базе есть факт likes(John,jazz) ), и, следовательно, результат нашего запроса - ложь. 4.6.3. Предикат ! - отсечение (cut) Действие этого предиката сводится к тому, что, условно говоря, "уничтожаются" все точки бектрекинга, отмеченные ранее в том правиле, где выполняется предикат, а также в вызывающей подцели. Если после выполнения предиката "!" какая-либо из следующих за ним подцелей завершается с результатом "ложь", то попытки повторного выполнения подцелей, лежащих между заголовком правила и предикатом "!", делаться не будет. Не будет также сделано попыток найти другое правило для вычисления вызывающей подцели. Примеры. 1) На следующем рисунке показан результат отсечения во время выполнения программы. В данном примере _A, _B, _C, _D, _E, _F - переменные, которые представляют цели в предикатах. _D, _E, _F - подцели цели _. Begin :- _A, _B, _C. _B :- _D, _E, cut, _F ┌────────┐┌─┐┌────┐ │ V│ V│ │ │ BEGIN:-_A,_B,_C.│ │ ┌─────────┘_A│_A│ │ │ └─┘│ │ │ │ ┌─┐┌─┐┌─┐┌┘ │ │ V │ V│ V│ V│ │ │_B:-_D,_E, !,_F.<┘ └──────────────┘ Если _A успешна, делается попытка согласовать цель _В, которая состоит из подцелей _D, _E, _F. Когда программа встречает _E и затем cut, она после этого согласует _F и возвращается в Begin на _С. Если _С неуспешна, программа проходит опять через _В, пытаясь повторно согласовать _F. Если _F неуспешна, то _В тоже неуспешна, так как она содержит cut. В этом случае происходит бектрекинг к подцели _A. Заметим, что если при бектрекинге встречается cut, неуспешным является вся процедура , а не только предложение, в котором cut находится. 2) Пусть у нас есть следующая процедура, определяющая факториал числа (т.е. произведение всех чисел от 1 до данного, факториал нуля по определению равно единице): факт(0,1). факт(_N,_X) :- факт(ВЫЧ(_N-1),_Y),_X is _Y*_N. Если эту процедуру рассматривать отдельно, она будет для положительных _N работать правильно. Однако если она используется внутри какого-либо предложения на Прологе так, что к ней возможно повторное обращение в результате бектрекинга, может произойти следующее. Например: a :- b,факт(_N,_X),c. Пусть при выполнении "c" была неудача и происходит бектрекинг. Последней отмеченной точкой бектрекинга является точка перед вторым правилом для факториала, причем в момент, когда успешно согласовался факт "факт(_N,_X)", _N равнялся 0. После бектрекинга будет выполняться второе правило, причем при рекурсивном обращении к предикату "факт" _N будет иметь значение -1. Далее программа зациклится на рекурсивных обращениях к " факт", причем аргумент _N будет принимать отрицательные значения, увеличивающиеся на единицу по модулю, и таким образом согласования с фактом "факт(0,1)." никогда не произойдет. Чтобы этого не произошло, процедуру для факториала надо написать так: факт(0,1) :- !. факт(_N,_X) :- факт(ВЫЧ(_N-1),_Y),_X is _Y*_N. В этом случае при бектрекинге повторного обращения к процедуре факториала не произойдет. 3) По аналогии с предыдущим примером процедуру считывания символов, приведенную в подразделе 4.5. следует написать так: rdb :- get(_X), _X = 66, !, write(_X). rdb :- rdb. 4.6.4. Предикат ЛОЖЬ ( fail ) Результат: всегда "ложь". Этот предикат часто используется не сам по себе, а в сочетании с другими предикатами. В частности, распространенным случаем является использование этого предиката в сочетании с отсечением. Например, с помощью этой пары можно определить встроенный предикат НЕ (not) на Прологе: not(_X) :- call(_X), !, fail. not(_X). Пусть введен запрос ?- not(_X). Если цель _X успешна, выполняется отсечение, а затем предикат fail. Предикат fail обеспечивает отрицательный результат, а отсечение не дает возможность пытаться согласовать его повторно. Если же цель _X неуспешна, происходит бектрекинг ко второму предложению, которое является фактом, сопоставляющимся с запросом, что дает конечный результат "истина". 4.6.5. Предикат (_X,_Y) _X , _Y - символьные константы или структуры. Предикат реализует логическую операцию конъюнкции ("и") над подцелями. Терм "_Х" рассматривается как подцель и выполняется. Если результат - "ложь", то результат всего предиката - "ложь". Если результат - "истина", то терм "_У" рассматривается как подцель и выполняется. Результат всего предиката есть результат подцели "_У". Замечание: функтор '.' определен как левоассоциативная инфиксная операция, т.е. _X,_Y,_Z эквивалентно (_X,_Y),_Z. 4.6.6. Предикат _Х ; _У _Х,_У - символьные константы или структуры. Результат: предикат реализует операцию дизъюнкции ("или") над подцелями. Терм _Х рассматривается как подцель и вычисляется. Если результат ее выполнения "истина", то результат всего предиката - "истина". Если он - "ложь", то вычисляется подцель _У и ее результат есть результат всего предиката. Замечание: функтор ';' определен как правоассоциативная инфиксная операция, т.е. _X;_Y;_Z эквивалентно _X;(_Y;_Z). Пример. Приведенная ниже процедура определяет принадлежность элемента списку. Элемент принадлежит списку, если он является головой списка или если он является элементом хвоста списка: элемент(_X,[_H|_T]) :- (_X = _H ; элемент(_X,_T)). Эту процедуру можно было бы написать иначе: элемент(_X,[_X|_]). элемент(_X,[_|_T]) :- элемент(_X,_T). Этот вариант более традиционен и, возможно, проще для чтения. Заметим, что хотя имеется ряд случаев, когда программа, содержащая отсечения, не может быть преобразована в обычную программу без использования дизъюнкции, в общем случае часто использовать ';' не рекомендуется. 4.6.7. Предикат ТРАС ( trace ) Результат: включение режима трассировки, в котором отслеживается выполнение подцелей. При этом, если не было обращения к предикату СЛЕД (spy), отслеживается выполнение ВСЕХ подцелей. Режим трассировки рассмотрен в разделе 5. 4.6.8. Предикат НЕТРАС ( notrace ) Результат: выключение режима трассировки. 4.6.9. Предикат СЛЕД _T ( spy _T ) _T - символьная константа. _T рассматривается как имя предиката и включается в список трассировки. После этого в режиме трассировки будет выдаваться информация только по этому предикату. Всего может быть до 10 отслеживаемых предикатов, определенных с помощью СЛЕД или spy. Предикат СЛЕД ( spy ) определен как префиксная операция. Ошибки: _T - не символьная константа. Пример. Пусть дана программа, выполняющая реверсирование списка, которая состоит из двух процедур - 'reverse', выполняющей собственно реверсирование, и 'append', выполняющей склеивание списков: reverse([],[]). reverse([_H|_T],_X) :- reverse(_T,_Y), append(_Y,[_H],_X). append([],_L,_L). append([_H|_T1],_L,[_H|_T2]) :- append(_T1,_L,_T2). Допустим, нас интересует только выполнение предиката reverse. Тогда можно написать так: ?- trace, spy reverse, reverse([1,2,3,4,5],_X). 4.6.10. Предикат НЕСЛЕД _T ( nospy _T ) _T - символьная константа. _T рассматривается как имя предиката и исключается из списка трассировки. Если при этом список трассировки остался непустым, будут отслеживаться предикаты, которые есть в этом списке. Если же он остался пустым, будут отслеживаться ВСЕ предикаты. Предикат НЕСЛЕД ( nospy ) определен как префиксная операция. Ошибки: _T - не символьная константа. 4.6.11. Предикат ВЫХОД quit Результат: немедленное прекращение выполнения программы. 4.7. Предикаты работы с динамической базой Эти предикаты оперируют с так называемой динамической базой данных Пролога. Эта база содержит факты и правила, которые могут динамически добавляться к ней и исключаться из нее. К данной группе встроенных предикатов относятся: ЗАГРУЗ consult УДАЛ retract ДОБ assert ДОБКОН assertz ДОБНАЧ asserta КЛОЗ clause ОПЕР op ВЫВПРЕД listing 4.7.1. Предикат ЗАГРУЗ(_F) ( consult(_F) ) _F - символьная константа. Если предикат ЗАГРУЗ или consult появляется в отдельном запросе, то он обрабатывается следующим образом: Значение _F задает имя файла, содержащего исходный текст на языке Пролог. После выполнения этого предиката следующие строки будут вводится из этого файла, который может содержать в себе клозы, комментарии и запросы. Они обрабатываются точно так же, как если бы были введены из редактируемого файла. Вводимый файл может содержать предикаты ЗАГРУЗ или consult. После завершения обработки файла входной поток возвращается к тому источнику ввода, где был вызван предикат. После выполнения предиката ЗАГРУЗ или consult остаток входной строки игнорируется. Для файлов, содержащих тексты на языке Пролог, подразумевается по умолчанию расширение .PRO. Однако можно указать явно и расширение и диск, с которого должен считываться файл. Допускается 3 уровня вложения файлов. Ошибки: _F не символьная константа отсутствует файл с заданным именем превышена глубина вложения файлов Пример: ?- consult('prog1.pro'). 4.7.2. Предикат УДАЛ(_C) ( retract(_C) ) _С - символьная константа или структура. Результат: уничтожение первого найденного клоза из динамической базы, заголовок которого унифицируется со структурой _С (символьная константа _С трактуется, как структура без элементов). Если клоз удален, то результат - "истина", иначе - "ложь". Этот предикат образует точку бектрекинга, при возврате к которой делается повторный просмотр динамической базы и попытка удалить следующий клоз, унифицируемый с _C. Связывания, сделанные при унификации,можно использовать после выполнения предиката. Ошибки: _C - не структура и не символьная константа. Пример. Пусть в базе данных Пролога есть следующая программа: pr(1) :- pr(2). pr(2). Если ввести следующий запрос: ?- УДАЛ(pr(2)),pr(_X). ответ будет: Нет 4.7.3. Предикаты ДОБ(_C), ДОБКОН(_C), ДОБНАЧ(_C), ( assert(_C), assertz(_C), asserta(_C) ) _С - структура (символьная константа) или клоз. Клоз _С, рассматриваемый как факт или правило, добавляется к динамической базе. Новый клоз вставляется в конец списка фактов (ДОБ, ДОБКОН, assert, assertz) или в начало (ДОБНАЧ, asserta). Предикат может применяться для добавления как фактов, так и правил. Ошибки: _С не может рассматриваться как клоз. Пример. Пусть в базе данных Пролога есть следующая программа: pr(1) :- pr(3). pr(2). Если ввести следующий запрос: ?- pr(_X). ответ будет _X = 2 Однако если ввести следующий запрос: ?- assert(pr(3)),pr(_X). ответ (в режиме поиска всех решений) будет следующим: _X = 1 _X = 2 4.7.4. Предикат КЛОЗ(_H,_T) ( clause(_H,_T) ) _H - структура или символьная константа, _T - структура (которую можно трактовать как тело правила) или свободная переменная. Этот предикат выполняет сопоставление аргументов _H и _T с головой и телом некоторого клоза, имеющегося в базе. Если такого клоза в базе нет, результат предиката - "ложь". Если таких предикатов несколько, то выбирается первое из них. В этом случае при попытке повторного согласования этого предиката после бектрекинга будет выбираться следующий клоз и т.д. Ошибки: _H - не структура и не символьная константа _T нельзя представить как тело правила. Замечание: хотя предикат КЛОЗ всегда имеет 2 аргумента, в базе есть клозы, которые не имеют тела (т.е. факты). Считается, что такой клоз имеет фиктивное тело 'true' ("истина"). В этом случае, если второй аргумент предиката КЛОЗ является свободной переменной, она конкретизируется символьной константой 'true'. Пример. Пусть в базе данных пролога есть программа склеивания списков: append([],_L,_L). append([_H|_T1],_L,[_H|_T2]) :- append(_T1,_L,_T2). Если ввести запрос: ?- КЛОЗ(append(_A,_B,_C),_Y). то в режиме поиска всех решений ответ будет следующий: _A = [] _B = _1 _C = _1 _Y = true _A = [_1|_2] _B = _3 _C = [_1|_4] _Y = append(_2,_3,_4) 4.7.5. Предикат ОПЕР(_P,_S,_A) ( op(_P,_S,_A) ) _P - целое число в диапазоне от 0 до 255 (включительно), _A - произвольная символьная константа (отличная от имен встроенных предикатов и встроенных операций), _S - символьная константа, принимающая одно из следующих значений: xfx xfy yfx yfy fx fy xf yf Этот предикат объявляет на Прологе операцию с приоритетом, задаваемым аргументом _P, позицией и ассоциативностью, задаваемыми аргументом _S и именем, задаваемым аргументом _A. Ошибки: _P не число _S не символьная константа указанного вида _A не символьная константа _A - имя встроенного предиката или встроенной операции Спецификации xfx xfy yfx yfy задают инфиксные операции, т.е. операции, у которых знак операции находится между двумя аргументами. Спецификации fx fy задают префиксные операции, т.е. операции, у которых знак операции находится перед единственным аргументом. Спецификации xf yf задают постфиксные операции, т.е. операции, у которых знак операции находится после единственного аргумента. Буквы 'x' и 'y' задают ассоциативность операции. В предположении, что выражение не содержит скобок, буква 'y' указывает, что соответствующий аргумент может содержать операции с приоритетом, равным приоритету данной операции или с более низким приоритетом. Буква 'x' указывает, что соответствующий аргумент может содержать операции со строго более низким приоритетом по сравнению с приоритетом данной операции. Операция со спецификацией yfx является левоассоциативной, а операция со спецификацией xfy - правоассоциативной. Например, если операция OPER объявлена со спецификацией yfx, то для нее a OPER b OPER c эквивалентно (a OPER b) OPER c Если же операция OPER объявлена со спецификацией xfy, то для нее a OPER b OPER c эквивалентно a OPER (b OPER c) Если операция OPER объявлена со спецификацией fy, то для нее синтаксически допустимой является следующая запись: OPER OPER a Эта запись будет синтаксически недопустимой, если операция объявлена со спецификацией fx. И аналогично, если операция OPER объявлена со спецификацией yf, то для нее синтаксически допустимой является следующая запись: a OPER OPER и эта запись будет синтаксически недопустимой, если операция объявлена со спецификацией xf. Пример: ?- ОПЕР(100,yfx,nop), write(1 nop 2 nop 3). Результат: на терминал выведется терм: (1 nop 2) nop 3 4.7.6. Предикат ВЫВПРЕД(_F) ( listing(_F) ) _F - символьная константа или структура Результат: вывод в выходной файл (на экран, на печать или в файл) тех клозов динамической базы, заголовки которых унифицируются с _F. При выводе символьные константы всегда изображаются строками, заключенными в апострофы, свободные переменные выводятся в виде символа подчеркивания, за которым следует некоторое число, уникальное в пределах одного клоза. Файл, выведенный предикатом ВЫВПРЕД или listing, может впоследствии быть загружен в динамическую базу предикатом ЗАГРУЗ или consult. Ошибки: _F - не символьная константа и не структура Пример. Пусть в базе данных пролога есть программа склеивания списков: append([],_L,_L). append([_H|_T1],_L,[_H|_T2]) :- append(_T1,_L,_T2). Если ввести запрос: ?- listing(append). на экран выведется следующий текст: append([],_1,_1). append([_1|_2],_3,[_1|_4]) :- append(_2,_3,_4). 4.8. Графические предикаты К данной группе встроенных предикатов относятся: ГРАФИКА initgraph ТЕКСТ closegraph КООРД coord ЦВЕТ color ТОЧКА pixel ЛИНИЯ line ОКРУЖ circle ПРЯМОУГ rectangle ЗАКРАСКА fillflood 4.8.1. Предикат ГРАФИКА ( initgraph ) Устанавливается графический режим работы системы. Выход из графического режима осуществляется при выполнении предиката ТЕКСТ (closegraph). 4.8.2. Предикат ТЕКСТ ( closegraph ) Результат: переход из графического режима в текстовый. После выполнения этого предиката повторный переход в графический режим может осуществляться только с помощью предиката ГРАФИКА. Для того чтобы после выполнения предиката ТЕКСТ графическое изображение не исчезло с экрана сразу, предикат выполняется следующим образом: на экран в графическом режиме выводится сообщение "Для возврата в текстовый режим нажмите на любую клавишу." После этого система ожидает нажатия клавиши. И только после того , как клавиша будет нажата, произойдет переход в текстовый режим. 4.8.3. Предикат КООРД(_X,_Y) ( coord(_X,_Y) ) _X - число или свободная переменная. _Y - число или свободная переменная. 1) Если _X - свободная переменная, она унифицируется с горизонтальной координатой текущей точки. Если _X - число, устанавливается соответствующее значение данной координаты текущей точки. 2) Если _Y - свободная переменная, она унифицируется с вертикальной координатой текущей точки. Если _Y - число, устанавливается соответствующее значение данной координаты текущей точки. Замечание: Сразу после установки графического режима текущей точкой считается точка, находящаяся в середине экрана, т.е. точка, имеющая координаты maxX/2, maxY/2. После выполнения предикатов, осуществляющих вывод на экран изображений, положение текущей точки меняется в зависимости от вида предиката. Ошибки: _X или _Y - не свободная переменная и не число. Примеры: 1) ?- ГРАФИКА, КООРД(_X,_Y). Ответ: _X = 320 _Y = 240 2) ?- initgraph,coord(_X,100). Ответ: _X = 320 Текущее значение _Y устанавливается равным 100. 4.8.4. Предикат ЦВЕТ(_C1,_C2) ( color(_C1,_C2) ) _C1, _C2 - свободные переменные или числа в диапазоне 0 - 15. Результат: если _С1 - число, то производится установка указанного цвета переднего фона, так что все далее выводимые фигуры будут изображаться этим цветом. Если _С1 - переменная, ей присваивается значение текущего цвета переднего фона, ранее установленного этим предикатом. Параметр _C2 аналогично определяет цвет заднего фона. Таблица цветов: Черный = 0 Темно-серый = 8 Голубой = 1 Светло-голубой = 9 Зеленый = 2 Светло-зеленый = 10 Бирюзовый = 3 Светло-бирюзовый = 11 Красный = 4 Светло-красный = 12 Лиловый = 5 Светло-лиловый = 13 Коричневый = 6 Желтый = 14 Светло-серый = 7 Белый = 15 Ошибки: _C1 или _C2 - не свободная переменная и не число в указанном диапазоне. Примеры: ?- ГРАФИКА,ЦВЕТ(1,4),ЦВЕТ(_X,_Y),ТЕКСТ. Ответ: _X = 1 _Y = 4 4.8.5. Предикат ТОЧКА(_X,_Y,_C) ( pixel(_X,_Y,_C) ) _X,_Y - координаты точки на экране (числа или свободные переменные) _С - цвет точки ( число или свободная переменная) Результат зависит от сочетания типов аргументов. 1) _X, _Y, _С - числа. На экран выводится точка с координатами _X, _Y цветом _C. 2) _X, _Y - числа, _С - свободная переменная. _С присваивается цвет точки с координатами (_X,_Y). 3) _X, _C - числа, _Y - свободная переменная. Рисуется прямая линия с координатами (_X,0), (_X,maxY) цветом _C. 4) _Y, _C - числа, _X - свободная переменная. Рисуется прямая линия с координатами (0,_Y), (maxX,_Y) цветом _C. 5) _X, _Y - свободные переменные, _C - число. Весь экран заполняется цветом _C. Все остальные сочетания аргументов рассматриваются как ошибка. После выполнения этого предиката, если аргументы _X, _Y конкретизированы, их значения становятся текущими координатами. Если какая-либо из них не конкретизирована, текущим значением этой координаты становится ее максимальное значение, т.е. maxX или maxY. Примеры: 1) ?- pixel(100,200,5),coord(_X,_Y). Ответ: _X = 100 _Y = 200 2) ?- pixel(_Z,100,3),coord(_X,_Y). Ответ: _Z = # _X = 640 _Y = 100 3.8.6. Предикат ЛИНИЯ(_X1,_Y1,_X2,_Y2,_C) ( line(_X1,_Y1,_X2,_Y2,_C) ) (_X1,_Y1) и (_X2,_Y2) - координаты двух точек на экране (числа или свободные переменные), _C - цвет (число). Результат зависит от сочетания типов аргументов. 1) Все аргументы - числа. Рисуется прямая линия из точки (_X1,_Y1) в точку (_X2,_Y2) цветом _С. 2) _Y2 - свободная переменная, остальные аргументы - числа. Рисуется закрашенный треугольник с вершинами (_X1,_Y1), (_X2,0), (_X2,Ymax) цветом _C. 3) _X2 - свободная переменная, остальные аргументы - числа. Рисуется закрашенный треугольник с вершинами (_X1,_Y1), (0,_Y2), (Xmax,Y2) цветом _C. 4) _Y1 - свободная переменная, остальные аргументы - числа. Рисуется закрашенный треугольник с вершинами (_X1,0), (_X1,Ymax), (_X2,Y2) цветом _C. 5) _X1 - свободная переменная, остальные аргументы - числа. Рисуется закрашенный треугольник с вершинами (0,_Y1), (Xmax,_Y1), (_X2,_Y2) цветом _C. 6) _X1, _Y1 -свободные переменные, остальные аргументы - числа; _X2, _Y2 -свободные переменные, остальные аргументы - числа; _X2, _Y1, _Y2 - свободные переменные, _X1, _C - числа; _X1, _X2, _Y2 - свободные переменные, _Y1, _C - числа; _X1, _Y1, _Y2 - свободные переменные, _X2, _C - числа; _X1, _Y1, _X2 -свободные переменные, _Y2, _C - числа; _C - число, остальные аргументы - свободные переменные. Во всех этих случаях весь экран закрашивается цветом _C. 7) _Y1, _Y2 - свободные переменные, остальные аргументы - числа. Рисуется вертикальный прямоугольник с вершинами (_X1,0), (_X1,Ymax), (_X2,0), (_X2,Ymax), закрашенный цветом _C. 8) _X1, _X2 - свободные переменные, остальные аргументы - числа; Рисуется вертикальный прямоугольник с вершинами (0,_Y1), (Xmax,Y1), (0,_Y2), (Xmax,Y2), закрашенный цветом _C. 9) _Y1, _X2 - свободные переменные, остальные аргументы - числа; Рисуется вертикальный прямоугольник с вершинами (_X1,0), (_X1,Ymax), (0,_Y2), (Xmax,Y2), закрашенный цветом _C. 10) _X1, _Y2 - свободные переменные, остальные аргументы - числа; Рисуется вертикальный прямоугольник с вершинами (0,_Y1), (Xmax,_Y1), (_X2,0), (_X2,Ymax), закрашенный цветом _C. Случай, когда _С - не число, рассматривается как ошибка. После выполнения этого предиката, если аргументы _X2, _Y2 конкретизированы, их значения становятся текущими координатами. Если какая-либо из них не конкретизирована, текущим значением этой координаты становится ее максимальное значение, т.е. maxX или maxY. Пример. Следующая программа рисует кирпичную стенку белым цветом: кирпич(_X,_Y) :- ЛИНИЯ(_X,_Y,ВЫЧ(_X+50),_Y,15), ЛИНИЯ(ВЫЧ(_X+50),_Y,ВЫЧ(_X+50),ВЫЧ(_Y+15),15), ЛИНИЯ(ВЫЧ(_X+50),ВЫЧ(_Y+15),_X,ВЫЧ(_Y+15),15), ЛИНИЯ(_X,ВЫЧ(_Y+15),_X,_Y,15). ряд(_X,_Y,0) :- !. ряд(_X,_Y,_n) :- кирпич(_X,_Y),ряд(ВЫЧ(_X+50),_Y,ВЫЧ(_n-1)). стенка(_X,_Y,_n,0) :- !. стенка(_X,_Y,_n,_h) :- ряд(_X,_Y,_n),ряд(ВЫЧ(_X+25),ВЫЧ(_Y-15),_n), стенка(_X,ВЫЧ(_Y-30),_n,ВЫЧ(_h-2)). 4.8.7. Предикат ОКРУЖ(_X,_Y,_R,_C) ( circle(_X,_Y,_R,_C) ) Результат зависит от сочетания типов аргументов. 1) Все аргументы - числа. Рисуется окружность радиуса _R с центром в точке (_X,_Y) цветом _С. 2) _X - свободная переменная, остальные аргументы - числа. Рисуется вертикальный прямоугольник с вершинами в точках (_X-_R,0), (_X-_R,Ymax), (_X+_R,0), (_X+_R,Ymax), закрашенный цветом _C. 3) _Y - свободная переменная, остальные аргументы - числа. Рисуется вертикальный прямоугольник с вершинами в точках (0,_Y-_R), (Xmax,_Y-_R), (0,_Y+_R), (Xmax,_Y+_R), закрашенный цветом _C. 4) _R - свободная переменная, остальные аргументы - числа; _Y, _R - свободные переменные, _X, _C - числа; _X, _R - свободные переменные, _Y, _C - числа; _X, _Y - свободные переменные, _R, _C - числа; _C - число, остальные аргументы - свободные переменные. Во всех этих случаях весь экран закрашивается цветом _C. Случай, когда _С - не число, рассматривается как ошибка. После выполнения этого предиката, если аргументы _X, _Y конкретизированы, их значения становятся текущими координатами. Если какая-либо из них не конкретизирована, текущим значением этой координаты становится ее максимальное значение, т.е. maxX или maxY. Пример. Следующая процедура рисует несколько вложенных окружностей с одним и тем же центром и радиусами, увеличивающимися на пять, красным цветом: окр(0,10). окр(_X,_n):-circle(350,200,_X,4),окр(eval(_X-5),_n). 4.8.8. Предикат ПРЯМОУГ(_X1,_Y1,_X2,_Y2,_C,_F) ( rectangle (_X1,_Y1,_X2,_Y2,_C,_F) ) Все аргументы должны быть числами. (_X1,_Y1), (_X2,_Y2) - координаты точек на экране, _C - цвет, _F - признак заполнения. Результат: рисуется прямоугольник с вершинами в точках (_X1,_Y1), (_X1,_X2), (_X2,_Y2), (_X2,_Y1) цвета _C. Если аргумент _F равен "1", прямоугольник закрашивается, в противном случае - нет. Ошибки: какой-либо аргумент - не число. После выполнения этого предиката текущей точкой становится точка с координатами _X2, _Y2. Пример: ?- rectangle(100,100,200,200,15,0). Результат: рисуется прямоугольник с вершинами в точках (100,100), (100,200), (200,200), (200,100) белым цветом, внутренняя часть его не закрашивается. 4.8.9. Предикат ЗАКРАСКА(_X,_Y,_C1,_C2) ( floodfill(_X,_Y,_C1,_C2) ) Все аргументы - числа. _X,_Y - координаты точки на экране, _С1 - цвет закраски, _С2 - цвет контура. Результат: если точка (_X,_Y) находится внутри замкнутого контура цвета _C2, внутренность контура заполняется цветом _C1. Если точка находится вне контура, то заполняется весь экран, исключая внутренность контура. Ошибки: какой-либо аргумент - не число. После выполнения этого предиката текущей точкой становится точка с координатами _X, _Y. Пример: ?- ОКРУЖ(200,200,100,5), ЗАКРАСКА(210,190,1,5). Результат: рисуется окружность с центром в точке (200,200) радиуса 100 лиловым цветом, и эта окружность закрашивается синим цветом. 4.9. Предикаты обработки символьных констант Эта группа встроенных предикатов работает с символьными константами, определенными в подразделе 2. К этим предикатам относятся следующие: БУКВА letter ЦИФРА digit СЦЕПЛЕН concat КОПИР copy ДЛИНА length Замечание 1. Если символьная константа начинается с символа "#", она считается эквивалентной символьной константе, содержащей те же символы, что и данная, но без символа '#'. Это делается для того, чтобы система могла отличать имена встроенных предикатов (в которых использование символа '#' не допускается) от совпадающих с ними по написанию символьных констант, выступающих как аргументы предикатов этой группы. Например, если ввести следующий запрос: ?- СЦЕПЛЕН('#functor',abc,_X),write(_X) на терминал выведется константа functorabc Если же ввести такой же запрос без символа '#', т.е. ?- СЦЕПЛЕН(functor,abc,_X),write(_X) на терминал выведется сообщение об ошибке в аргументах встроенного предиката functor. При выполнении предикатов, определяющих длину или позицию в символьных константах, место, занимаемое символом, '#' не учитывается. Замечание 2. Во всех предикатах, определяющих позицию символов в символьных константах, считается, что позиция первого символа равна НУЛЮ (0). 4.9.1. Предикат БУКВА(_A,_N) ( letter(_A,_N) ) _A - символьная константа, _N - свободная переменная или целое число. Результат зависит от сочетания типов аргументов. 1) _N - свободная переменная. Если в константе _A есть буква, то результат "истина" и _N конкретизируется позицией самой левой буквы, в противном случае результат "ложь". 2) _N - число. Результат "истина", если символ константы в позиции, определяемой _N - буква, в противном случае - "ложь". Остальные сочетания типов аргументов рассматриваются как ошибка. Примеры: 1) ?- БУКВА('@*$%abcde',_X). Ответ: _X = 4 2) ?- letter('12345лорп'2). Ответ: Нет 3) ?- letter('#write'_X). Ответ: _X = 0 4) ? -letter(write,_X). Результат: выведется сообщение об ошибке в аргументах встроенного предиката. 4.9.2. Предикат ЦИФРА(_A,_N) ( digit(_A,_N) ) _A - символьная константа, _N - свободная переменная или целое число. Результат зависит от сочетания типов аргументов. 1) _N - свободная переменная. Если в константе _A есть цифра, то результат "истина" и _N конкретизируется позицией самой левой цифры, в противном случае результат "ложь". 2) _N - число. Результат "истина", если символ константы в позиции, определяемой _N - цифра, в противном случае - "ложь". Остальные сочетания типов аргументов рассматриваются как ошибка. Примеры: 1) ?- ЦИФРА('ab1cd2',2). Ответ: Да 2) ?- letter('abc*&$#'_X). Ответ: Нет 4.9.3. Предикат СЦЕПЛЕН(_A,_B,_C) ( concat(_A,_B,_C) ) _A, _B, _C - свободные переменные или символьные константы. Результат зависит от сочетания типов аргументов. 1) _A, _B - символьные константы, _C - переменная или символьная константа. Вычисляется конкатенация _A и _B и результат унифицируется с _C. Результат предиката есть результат этой унификации. 2) _A, _C - символьные константы, _B - свободная переменная. Если _C можно представить как результат конкатенации _A с какой-либо символьной константой, результат "истина" и _B унифицируется с этой константой, в противном случае результат "ложь". 3) _B, _C - символьные константы, _A - свободная переменная. Если _C можно представить как результат конкатенации какой-либо символьной константы с _B, результат "истина" и _A унифицируется с этой константой, в противном случае результат "ложь". Остальные сочетания типов аргументов рассматриваются как ошибка. Примеры: 1) ?- СЦЕПЛЕН(абвгд,ежзик,_X). Ответ: _X = абвгдежзик 2) ?- concat(abcde,fghijk,accd). Ответ: Нет 3) ?- concat('123',_X,'12345678'). Ответ: _X = '45678' 4.9.4. Предикат КОПИР(_A,_N1,_N2,_B) ( (copy(_A,_N1,_N2,_B) ) _A - символьная константа, _B - символьная константа или свободная переменная, _N1, N2 - числа или свободные переменные. Результат зависит от сочетания типов аргументов. 1) N1, N2 - числа. Из _A выделяется подстрока с позиции _N1 длиной _N2 и результат унифицируется с _B. Результат предиката есть результат этой унификации. Если числа _N1 и _N2 таковы, что их сумма больше длины символьной константы _A, то в унификации участвует подстрока меньшей длины. 2) _N1 - переменная, N2 - число, _B - символьная константа. Если длина _B равна _N2 и _B входит в _A с некоторой позиции, то результат "истина" и _N1 унифицируется с соответствующим номером позиции, в противном случае результат "ложь". 3) _N1, _N2 - переменные, _B - символьная константа. Если _B входит в _A с некоторой позиции, то результат "истина", при этом _N1 унифицируется с соответствующим номером позиции, _N2 - с длиной _B, в противно случае результат "ложь". 4) _N1 - число, _N2 - переменная, _B - символьная константа. Если _B входит в _A с позиции _N1, то результат "истина", при этом _N2 унифицируется с длиной _B, в противно случае результат "ложь". Остальные сочетания типов аргументов рассматриваются как ошибка. Примеры: 1) ?- КОПИР(abcdefgh,2,5,_X). Ответ: _X = cdefg 2) ?- copy('12345678',_X,3,abc). Ответ: Нет 3) ?- КОПИР('1234567890',_X,_Y,'3456'). Ответ: _X = 2 _Y = 4 4) ?- copy(_X,2,4,abcde) Результат: Ошибка (т.к. первый аргумент - не символьная константа) 4.9.5. Предикат ДЛИНА(_A,_N) ( length(_A,_N) ) _A - символьная константа, _N - целое число или свободная переменная. _N унифицируется c длиной _A. Результат предиката есть результат этой унификации. Остальные сочетания типов аргументов рассматриваются как ошибка. Примеры: 1) ?- ДЛИНА(#abcde,_X). Ответ: _X = 5 (т.к. символ '#' не учитывается) 2) ?- length('12345678',5). Ответ: Нет 4.10. Прочие предикаты К прочим предикатам относятся следующие: СЛУЧ random УСТ_ВРЕМЯ settime ЧИТ_ВРЕМЯ gettime 4.10.1. Предикат СЛУЧ(_X) ( random(_X) ) _X - свободная переменная. _X унифицируется со случайным целым числом в диапазоне 0 .. 32767. Результат - "истина". Ошибки: _X - не свободная переменная. Пример: ?- random(_X),write(_X). Результат: На терминал выведется некоторое целое число. 4.10.2. Предикат УСТ_ВРЕМЯ ( settime ) Этот предикат устанавливает начало отсчета времени. В дальнейшем при выполнении предиката ЧИТ_ВРЕМЯ или gettime читается время, прошедшее от данного момента. Поэтому данный предикат всегда нужно вызывать перед первым вызовом предиката ЧИТ_ВРЕМЯ. 4.10.3. Предикат ЧИТ_ВРЕМЯ(_X) ( gettime(_X) ) _X - свободная переменная. _X унифицируется с величиной интервала времени, прошедшего от последнего вызова предиката УСТ_ВРЕМЯ (или settime) в сотых долях секунды (вещественное число). Результат - "истина". Если ранее предикат settime не вызывался, предикат gettime выполняется и возвращает в _X непредсказуемое значение. Ошибки: _X - не свободная переменная. Пример: ?- settime,factorial(5,_X),gettime(_Y). Ответ: _Y = < некоторое вещественное число - время выполнения процедуры factorial > 5. Работа с системой PROLOGUS 5.1. Запуск системы Для запуска системы PROLOGUS нужно ввести команду PROLOGUS После загрузки системы сразу открывается окно редактора, в котором можно вводить текст новой программы на Прологе либо загружать существующий файл. Загрузку файла можно осуществить либо нажатием клавиши F3, либо с помощью главного меню. Для входа в главное меню нужно нажать клавишу ESC. Для получения подсказки нужно нажать клавишу F1. В системе PROLOGUS реализован режим контекстно-зависимой подсказки (по встроенному предикату, имя которого в данный момент находится под курсором). Для ее получения нужно нажать клавиши Ctrl F1. Если нажать клавиши Alt F1, будет показан последний из показанных ранее пунктов подсказки. Нажимая Alt F1 последовательно несколько раз, можно проследить в обратном порядке последовательность выводов на экран пунктов подсказки. Строки, начинающиеся с символов % @ $ &, являются комментариями. Они не учитываются интерпретатором Пролога. Все символы комментариев, кроме %, можно "отменить". Для этого надо нажать комбинацию этой клавиши и клавиши Alt. Повторное нажатие этих двух клавиш возвращает исходную ситуацию. Если знак комментария "отменен", он рассматривается системой как пробел. 5.2. Главное меню В данной версии системы PROLOGUS реализованы следующие функции главного меню: Файл Запуск Редактор Отладка Поиск Опции Выход 5.3. Работа с файлами Данный пункт меню позволяет выполнять следующие операции над файлами: Загрузить - дублируется клавишей F3 Сохранить - дублируется клавишей F2 Сохранить как В первом и третьем случае система запрашивает имя файла. Если в ответ ввести маску имени файла, на экране появится два окна, в одном из которых можно выбрать дисковод, а в другом - директорию и имя файла (аналогично тому, как это делается в Norton Commander). Переход от одного окна к другому осуществляется с помощью клавиши "Tab". 5.4. Редактирование файла Значения клавиш управления курсором: стрелки, Home, PgUp, End, PgDn, Ins, Del - общепринятые. Кроме того, реализованы следующие управляющие клавиши: Esc - войти в главное меню Ctrl Y - стереть строку Ctrl E - стереть от курсора до конца строки Ctrl Q - перейти на заданную строку Ctrl -> - переход на слово вправо Ctrl <- - переход на слово влево Ctrl PgUp - переход на начало текста Ctrl PgDn - переход на конец текста Ctrl K B - начало выделения блока Ctrl K K - конец выделения блока Ctrl K C - скопировать блок Ctrl K V - переместить блок Ctrl K Y - стереть блок F1 - подсказка (индекс) F2 - сохранить файл F3 - открыть файл F4 - поиск слова F9 - запуск программы Ctrl F1 - подсказка (контекстная) Ctrl F4 - повторный поиск слова Alt F1 - подсказка (последний пункт) Alt F5 - переключение экрана Alt @ - переключение комментария "@" Alt $ - переключение комментария "$" Alt & - переключение комментария "&" 5.5. Поиск строки Данный пункт меню позволяет выполнять следующие операции: Поиск Замена Найти следующее Переход на строку В первых двух случаях система запрашивает имя строки, в последнем - номер строки. 5.6. Запуск выполнения программы Данный пункт меню позволяет запустить программу на Прологе, текст которой в данный момент находится в окне редактора, на выполнение. Эта функция дублируется клавишей F9. После этого окно редактора закрывается и система переходит в режим диалога с интерпретатором Пролога, в котором система выводит приглашение, сообщения об ошибках, а также информацию, выводимую предикатами вывода термов (см. подраздел 4.5.), а пользователь может осуществлять ввод запросов, а также информации, требуемой при выполнении предикатов ввода термов (см. подраздел 4.5.). Ввод запросов осуществляется после того, как система вывела приглашение, которое имеет вид: ?- Это происходит в двух случаях: во-первых, если в тексте, находящемся в окне редактора, нет запроса, приглашение выводится срезу после перехода в режим диалога. Кроме того, приглашение выводится после того, как все запросы, находящиеся в тексте окна редактора, выполнены. Если система работает в режиме поиска одного решения и в запросе есть переменные, после вывода первого решения можно получить альтернативные решения, нажав клавишу ";" (и затем нажимая ее после вывода каждого очередного решения). Когда все решения будут выведены, система выведет слово "Нет". Это не должно беспокоить пользователя. Если система работает в режиме поиска всех решений, все возможные решения выводятся автоматически без нажатия клавиши ";". Возврат в окно редактора происходит при нажатии клавиши "F10". 5.7. Трассировка программы Данный пункт меню позволяет запустить программу на Прологе в режиме трассировки. Эта функция дублируется встроенным предикатом ТРАС ( или trace ). В режиме трассировки происходит отслеживание всех выполняемых подцелей или предикатов, включенных в список трассировки предикатом СЛЕД или spy (см. подраздел 4.7.). Для каждой отслеживаемой подцели фиксируются следующие моменты: Вызов ВЫЗ < имя цели > Попытка повторного согласования ПОВТ < имя цели > Успешное согласование ВЫХ < имя цели > Неудача НЕУД < имя цели > После вывода каждой строки указанного вида для дальнейшего продолжения выполнения программы надо нажимать клавишу ENTER. В режиме трассировки пользователь может вводить следующие однобуквенные команды: П - вывести объем свободной памяти в байтах Н - выйти из режима трассировки. Кроме того, выход из режима трассировки осуществляется при выполнении предиката НЕТРАС (или notrace). 5.8. Установка опций работы системы Данный пункт меню позволяет установить следующие опции: Поиск одного решения / Поиск всех решений Плавная прокрутка / Быстрая прокрутка Подсветка включена / Подсветка выключена При выборе этого пункта меню на экране появляется окно опций, в котором записаны имена всех указанных опций, а напротив имени - текущее значение опции (текущее значение - это либо значение, назначенное по умолчанию, либо установленное ранее в этом окне). Выбор опции осуществляется циклически путем нажатия клавиши ENTER в момент, когда имя этой опции засвечено. Для того чтобы зафиксировать выбранную опцию, нужно подогнать засветку к слову "Ok" и снова нажать ENTER.