Язык логического программирования
Первой находкой создателей языка
Второй находкой, перенесенной авторами языка
Третья находка языка
Четвертая находка создателей языка

обладают важным свойством. Для нахождения вывода в системе
Когда рассматривается исполнение программы в нетрадиционном языке (например,
Данные, используемые
Рассмотрим на уровне
В
И, наконец, в
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
Предложение состоит из головного выражения (соответствующего заключению хорновской формулы) и его раскрытия: нескольких выражений, соединенных как последовательно достигаемые подцели
В любой момент исполнения программы
Теперь перейдем к конкретному представлению данных.
В конкретном синтаксисе переменные языка представлены именами, состоящими из букв и начинающимися с большой буквы либо с символа подчеркивания _. Переменная _ называется анонимной переменной и считается различной во всех своих вхождениях.
Константы языка <, =, $ ), символы и числа (символы отождествляются с целыми числами, являющимися их кодами). Произвольная последовательность символов может быть сделана единой константой, например:
'C:\"SICS Prolog"\program.pl'.
Любое константное имя может служить write(a(1)) и write(a(1),file1) используют различные x+y означает в точности то же, что и +(x,y).
Для некоторых из предопределенных в системе функций и предикатов имеются дополнительные load(f) f должно быть именем файла.
|, в этом случае подцели "альтернативны".Наиболее важным и классическим является случай последовательно достигаемых подцелей, через который определяется и семантика альтернативных
В конкретном представлении предложение, например,
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
также рассматривается как терм, поскольку имена , (здесь символ запятой — имя операции) и :- рассматриваются как инфиксные операции, причем запятая связывает сильнее.
Как правило, предложения, относящиеся к одному и тому же предикату, группируются вместе, например:
parent(X,Y) :- mother(X,Y). parent(X,Y) :- father(X,Y).
Порядок предложений существенен.
father(ivan,vasilij):-true.
Здесь мы встретились с одной из двух стандартных целей: true обозначает очевидную удачу, а fail — очевидную неудачу.
Во-вторых, специально для фактов имеется скоропись, означающая то же самое:
father(ivan,vasilij).
В принципе, все остальные структуры языка [] применением двухместного .(head,tail). Выстроенная в стандартном порядке композиция
.(a,.(b,. . . , .(z,[]). . . ))
понимается как линейный список и обозначается [a,b,. . . ,z].
Для обозначения присоединения нескольких данных термов к началу списка имеется стандартная операция
[t,u|L].
Строки рассматриваются как линейные списки кодов символов и обозначаются последовательностью символов, взятой в двойные кавычки:
"Ну, получили то, что искали? Ответьте y или n."
Заслуживает упоминания механизм введения новых операций в язык
:- op(1200,xfx, ':-'). :- op(1200,fx, [':-','?-']). :- op(1000,xfy, ','). :- op(700, xfx, [=,is,<,=<,==]). :- op(500, yfx, [+,-]). :- op(500, fx, [+,-,not]). :- op(400, yfx,[*,/,div]).
Первый аргумент в этих описаниях — приоритет операции. Он может быть от 1 до 1500. Второй аргумент — шаблон операции; x обозначает выражение с приоритетом, строго меньшим приоритета операции; y — выражение с приоритетом, который меньше или равен приоритету операции, f — положение самого символа операции относительно аргументов. Таким образом, шаблон yfx для операции - означает, что выражение X-Y-Z понимается как (X-Y)-Z, шаблон xfy для запятой означает, что t,u,r понимается как t,(u,r),шаблон xfx для :- означает невозможность использования нескольких таких операций подряд без дополнительных скобок. Операции с меньшими приоритетами связывают свои аргументы сильнее. Один и тот же атом может быть определен и как унарная, и как бинарная операция.
Пример описаний операций показывает, что даже локальное использование различения конкретно- и абстрактно-синтаксических представлений программы дает возможность получить большие преимущества. В
Джулией Робинсон доказано (см., напр. ), что для выражений первого порядка имеется эффективный алгоритм
Пример 6.3.1. Две последовательности выражений

где a, b — константы, а латинские буквы из конца алфавита — переменные, унифицируются в

подстановкой

А в двух последовательностях

никакие два соответственных выражения унифицированы быть не могут.
Уже в приведенном примере видно, что
Заметим, что логический алгоритм
Более того, можно было бы унифицировать любые соответственные друг другу внутренние
В первой реализации языка
Желающие в качестве упражнения выловить ошибку самостоятельно, сравните алгоритмы
Рассмотрим, как исполняется программа на языке :- или ?-. В программе, транслируемой и исполняемой в пакетном режиме, обычно используется первый
Исходная цель называется запросом. Переменные, входящие в запрос, носят особый статус. Их значения в ходе последовательных
В каждый момент рассматривается первый из термов цели. Если его детерминатив не является встроенной функцией или встроенным оператором с особым определением, то ищется предложение, голова которого унифицируется с этим термом. При этом прежде всего проверяется наличие предложений, детерминатив которых совпадает с детерминативом первого терма. Если таких предложений несколько, то создается точка
Предложения испытываются, начиная с первого. Полученная унифицирующая подстановка применяется ко всем термам в
Заметим, что переменные предыдущих
Если исполнение оказалось неудачным, то программа возвращается к последней из
Стандартным ответом программы на запрос служит Yes, если программа закончилась удачно, и No, если она закончилась неудачно. При удаче выводятся значения всех переменных исходного запроса.
Так, например, если программа и ее
greater(X,Y):-greater1(X,Y). greater(X,Y):-greater1(Z,Y),greater(X,Z). greater1(X,f(X)). estimation(X,Y):-greater(X,Y),known(Y). known(f(f(f(f(a))))). unknown(a). unknown(b).
то ответом на запрос
?-unknown(Y),estimation(Y,X).
будет
Y=a X=(f(f(f(f(a)))) Yes
а при попытке ответа на запрос
?-estimation(b,X).
программа зациклится.
Насколько каверзны вроде бы невинные предположения (например, условие, что подцели достигаются строго одна за другой и варианты перебираются в том же порядке), сделанные в языке
estimation(X,Y):-known(Y),greater(X,Y).
программа успешно ответит на второй запрос
No
Еще более впечатляющий пример рассмотрен в упражнении 5.
Есть еще одна особенность языка [X|Y] с уже известным списком Z X будет пустым списком), либо зациклится на бесконечном повторении одного и того же варианта (смотри предыдущую скобку, которая иллюстрирует сразу две неприятности: если к такой X ).
Для того, чтобы вычислить выражение, имеется предопределенная бинарная операция is. Она должна иметь вторым аргументом выражение, составленное из атомов при помощи функций. После применения X is 1+2 вместо X подставится 3. Даже выражение 1+2 остается в таком же виде, пока оно не попадет во второй аргумент is.
Внимание!
То, что некоторый функтор определен как операция, не значит, что он вычисляется. Это просто изменение конкретно-синтаксического представления. Для того чтобы иметь возможность вычислить выражение, нужно определить функтор как внутреннюю или внешнюю функцию. При этом необязательно делать его операцией.
Рассмотренные до сих пор средства языка
Пример 6.3.2. Рассмотрим, как с помощью ! и списков программируется поиск пути в лабиринте (и даже в произвольном ориентированном графе).
way(X,X,[X]). way(X,Y,[Y|Z]):-connect(U,Y), nomember(Y,Z),way(X,U,Z). way(X,Y,[Y|Z]):-connect(U,Y), way(X,U,Z). nomember(Y,Z):-member(Y,Z),!,fail. nomember(Y,Z). connect(begin,1). connect(1,begin). connect(1,2). connect(2,3). connect(3,1). connect(3,4). connect(4,end).
В ответ на запрос
?-way(begin,end,X).
программа выдаст
X = [end, 4, 3, 2, 1, begin] Yes
Вместо определения nomember можно написать предложение
way(X,Y,[Y|Z]):-connect(U,Y),not (member(Y,Z)),way(X,U,Z).
Предикат отсечения можно использовать для того, чтобы превратить
Внимание!
Некоторые из русскоязычных учебных пособий по языку
Прагматические соглашения о порядке выполнения действий в программе привели к тому, что если мы запишем в форме языка
A:-A.
и этот
Конечно же, несообразности были использованы и для получения новых эффектов. Рассмотрим следующее определение.
repeat. repeat:-repeat.
Если вставить теперь цель !, то эти подцели будут повторяться вплоть до удачи и их побочные эффекты будут исполняться в цикле.
Приведенное выше определение repeat писать в программах не нужно. В стандарте repeat, потенциально бесконечное число раз успешно унифицируемый. Реализованный в языке
Недетерминированную
Недетерминированным достижением цели называется успешное вычисление. Таким образом, если какая-то из последовательностей продолжений приводит к цели, то цель процесса считается достигнутой. Недетерминированная
Есть теорема, доказывающая, что в принципе недетерминированный конечный автомат всегда можно преобразовать в детерминированный. Идея преобразования — склейка состояний, как показано на рис. 6.1. При этом, содержательно говоря, мы создаем линейный порядок на множестве альтернатив и выбираем альтернативы в
(рис 6.1) Преобразование недетерминированного поиска в детерминированныйТеорема детерминирования конечного автомата обосновывает существование успешного вычисления. Но она не дает никаких хороших оценок изменения сложности вычислений при переходе к детерминированному поиску. И даже если успешное вычисление существует, это не означает, что трансформировать ассоциированные с переходами действия легко и что после трансформации они будут хоть сколько-нибудь понимаемы. По этой причине часто обработку удобнее описывать как недетерминированную, поручая решение задачи организации перебора вариантов системе программирования.
Поскольку структура программы и структура
Для этой цели в . Он читает предложения и факты из файла и помещает их в конец программы, тем самым оставляя в неприкосновенности ранее данные определения предикатов. С его использованием наша программа может быть переписана в следующем виде.
way0(X,Y,Z):-consult(labyr),way(X,Y,Z). way(X,X,[X]). way(X,Y,[Y|Z]):-connect(U,Y), not member(Y,Z),way(X,U,Z). way(X,Y,[Y|Z]):-connect(U,Y), way(X,U,Z).
Пример файла labyr.pl:
connect(begin,1). connect(1,begin). connect(1,2). connect(2,3). connect(3,1). connect(3,4). connect(4,end).
Программа 6.4.1 представляет лишь идею решения, но эту идею она представляет исключительно выразительно.
Есть еще один класс is. К ним, в частности, относятся многие действия над списками. Рассмотрим, например, предикат append(E1,E2,E3). Он корректно унифицируется, когда объединение первых двух списков является третьим. Соответственно, он может использоваться для вычисления любого из своих трех аргументов, если два других заданы. Например,
append(X,Y,Z)
при Z=[a,b,c,d], Y=[c,d] унифицируется как X=[a,b].
Очень жаль, что в
Для динамического порождения фактов и предложений имеются функции, разбирающие предложения и синтезирующие их.
Метапредикат помещает свой аргумент в , наоборот, удаляет из программы предложение или факт, унифицируемый с его аргументом.
С их помощью можно, в частности, имитировать различные более эффективные алгоритмы перебора для работы с лабиринтом, но при этом программа до некоторой степени теряет ясность структуры и становится крайне трудно ее отладить и модифицировать. А эффективности, сравнимой с традиционными методами, достичь все равно не удастся.
Но, например, если Вы анализируете сложную систему правил и ищете вывод, то результат анализа часто можно записать как файл динамически порожденных предложений, и это, наоборот, делает программу красивее, а ее отладку легче. Так что есть смысл использовать динамическое порождение в том случае, когда программа сначала коллекционирует и анализирует информацию, а лишь затем начинает действовать. А порождение, перемешанное с действиями, — кратчайший путь к провалу программы, и должно рассматриваться как хакерство.
Далее, после того, как использованы динамически порожденные факты или предложения, их можно удалить из программы при помощи предиката
retractall(Name / Arity)
Этот предикат удаляет все предложения и факты, говорящие о предикате Name
Внимание!
В новых версиях языка dynamic(connect).
Для проверки типов термов имеются, в частности, следующие встроенные предикаты.
var(Term). Унифицируется, если Term nonvar(Term). Унифицируется, если textsfTerm не integer(Term) Успешен, если Term является целым числом (именно числом, а не выражением).float(Term) Успешен, если Term является действительным числом.number(Term) Успешен, если Term является числом.atom(Term) Успешен, если Term является атомом.string(Term). Успешен, если Term является строкой.atomic(Term). Успешен, если Term является неделимым значением (число, строка или атом).compound (Term). Успешен, если Term является сложным выражением.ground (Term). Успешен, если Term не содержит Для анализа и построения термов имеются, в частности, следующие предикаты.
functor (Term, Functor , Arity ) Унифицируется, если Term является термом с главным Functor Arity . Term, являющийся переменной, унифицируется с новой переменной. Если Term является атомом либо числом, то его arg(Arg, Term, Value) Выделение аргумента терма Term по его номеру Arg.Номера начинаются с 1. Естественно, что данный предикат может быть использован и для определения номера аргумента в терме.Term =.. List Унифицируется, если List является списком, головой которого является Term, а оставшиеся члены задают аргументы (сравните с тем, что ниже рассматривается в языке LISP!) Если не использовать его как операцию, то имя этого предиката Univ. Естественно, он может работать в обе стороны, разбирая либо собирая терм.Примеры.
?- send(hello, X) =.. List. List = [send, hello, X] ?- Term=.. [send, hello, X] Term = send(hello,X)
free_variables(Term, List) List унифицируется как список новых переменных, каждая из которых равна свободной терма Term.
atom_codes(Atom, String) Преобразование атома в строку и наоборот.
Многие из реализаций языка
Во многих случаях даже в поисковой программе необходимо производить вычисления. В языке 1 + 1 + 1, но оно не будет равно 3.
Для организации вычисления имеется специальное отношение X is E. В этом отношении Е является таким выражением, которое после подстановки текущих значений переменных Х унифицируется как ее значение.
Таким образом, можно постепенно накапливать вычисления, а затем в подходящий момент их произвести. Смотрите пример.
?- assert(a(1+1)). Yes ?- assert(b(2 * 2)). Yes ?- a(X), b(Y), Z is X + Y. X = 1+1 Y = 2*2 Z = 6
Внимание!
is не является присваиванием! Для того, чтобы убедиться в этом, исполните простейшее предложение языка
?- X is 1, X is X + 1.
Лучше всего и естественней всего вводятся в .
Конечно же, имеется и более традиционная система ввода-вывода. Опишем ее базовые возможности.
open(SrcDest, Mode, Stream, Options)
Открытие файла. SrcDes является атомом, содержащим имя файла в обозначениях системы Unix. Mode может быть read, write, append или update. Два последних способа открытия используются, соответственно, для дописывания в существующий файл и для частичного переписывания его. Stream либо переменная, и тогда ей присваивается целое число, которое служит для идентификации файла, либо атом, и тогда он служит внутри программы именем файла. Options могут быть опущены, среди них нам важна одна опция: type(binary), которая позволяет записать коды в двоичный файл. Опции образуют список.
Конечно же, имеется возможность вручную установить текущую позицию внутри файла:
seek(Stream, Offset, Method, NewLocation)
Method — это метод отсчета относительной позиции. отсчитывает ее с начала файла, current от нынешней точки, eof от конца. Переменная NewLocation унифицируется с новой позицией, отсчитываемой обязательно с начала.
Предикат close(Stream) комментариев не требует.
read(Stream, Term)
Переменная Term унифицируется с термом, прочитанным из потока Stream.
read_clause(Stream, Term)
Читается предложение. По умолчанию пользователя предупреждают о переменных, которые отсутствуют в голове и лишь однажды присутствуют в хвосте.
read_term(Stream, Term, Options)
Аналогично read, но позволяет установить целый ряд возможностей, регулирующих представление терма. Смотрите подробнее в документации конкретной
writeq(Stream, Term)
Term пишется в Stream, вставляются кавычки и скобки, где нужно.
write_canonical(Stream, Term)
Term пишется в Stream таким способом, чтобы его однозначно прочитала любая
Есть способ читать и писать символы, а через них строки и прочее, но это настолько примитивно и уродливо, что можно дать практический совет:
Внимание!
Если Вам нужно ввести в
Тем не менее вот минимальный (и практически полный) список предикатов символьного и двоичного ввода и вывода.
get_byte(Stream, Byte)
Byte рассматривается как целое число и унифицируется со следующим байтом входного потока. Конец файла читается как -1.
get_char(Stream, Char)
Аналогично, но следующий байт рассматривается как имя атома, состоящее из одного символа. Конец файла унифицируется с атомом end_of_file. Русские буквы, пробелы и прочие нестандартные символы могут вызвать неприятности.
get(Stream, Char)
Аналогично, но пропускаются невидимые символы.
skip(Stream, Char)
Пропускает все, пока не встретится символ Char либо конец файла. Само первое вхождение Char также будет пропущено.
put(Stream, Char)
Вывод одного символа либо байта. Char унифицируется либо как целое число из диапазона [0, 255], либо как атом с именем из одного символа.
nl(+Stream)
Вывести перевод строки.
Язык логического программирования
Первой находкой создателей языка
Второй находкой, перенесенной авторами языка
Третья находка языка
Четвертая находка создателей языка

обладают важным свойством. Для нахождения вывода в системе
Когда рассматривается исполнение программы в нетрадиционном языке (например,
Данные, используемые
Рассмотрим на уровне
В
И, наконец, в
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
Предложение состоит из головного выражения (соответствующего заключению хорновской формулы) и его раскрытия: нескольких выражений, соединенных как последовательно достигаемые подцели
В любой момент исполнения программы
Теперь перейдем к конкретному представлению данных.
В конкретном синтаксисе переменные языка представлены именами, состоящими из букв и начинающимися с большой буквы либо с символа подчеркивания _. Переменная _ называется анонимной переменной и считается различной во всех своих вхождениях.
Константы языка <, =, $ ), символы и числа (символы отождествляются с целыми числами, являющимися их кодами). Произвольная последовательность символов может быть сделана единой константой, например:
'C:\"SICS Prolog"\program.pl'.
Любое константное имя может служить write(a(1)) и write(a(1),file1) используют различные x+y означает в точности то же, что и +(x,y).
Для некоторых из предопределенных в системе функций и предикатов имеются дополнительные load(f) f должно быть именем файла.
|, в этом случае подцели "альтернативны".Наиболее важным и классическим является случай последовательно достигаемых подцелей, через который определяется и семантика альтернативных
В конкретном представлении предложение, например,
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
также рассматривается как терм, поскольку имена , (здесь символ запятой — имя операции) и :- рассматриваются как инфиксные операции, причем запятая связывает сильнее.
Как правило, предложения, относящиеся к одному и тому же предикату, группируются вместе, например:
parent(X,Y) :- mother(X,Y). parent(X,Y) :- father(X,Y).
Порядок предложений существенен.
father(ivan,vasilij):-true.
Здесь мы встретились с одной из двух стандартных целей: true обозначает очевидную удачу, а fail — очевидную неудачу.
Во-вторых, специально для фактов имеется скоропись, означающая то же самое:
father(ivan,vasilij).
В принципе, все остальные структуры языка [] применением двухместного .(head,tail). Выстроенная в стандартном порядке композиция
.(a,.(b,. . . , .(z,[]). . . ))
понимается как линейный список и обозначается [a,b,. . . ,z].
Для обозначения присоединения нескольких данных термов к началу списка имеется стандартная операция
[t,u|L].
Строки рассматриваются как линейные списки кодов символов и обозначаются последовательностью символов, взятой в двойные кавычки:
"Ну, получили то, что искали? Ответьте y или n."
Заслуживает упоминания механизм введения новых операций в язык
:- op(1200,xfx, ':-'). :- op(1200,fx, [':-','?-']). :- op(1000,xfy, ','). :- op(700, xfx, [=,is,<,=<,==]). :- op(500, yfx, [+,-]). :- op(500, fx, [+,-,not]). :- op(400, yfx,[*,/,div]).
Первый аргумент в этих описаниях — приоритет операции. Он может быть от 1 до 1500. Второй аргумент — шаблон операции; x обозначает выражение с приоритетом, строго меньшим приоритета операции; y — выражение с приоритетом, который меньше или равен приоритету операции, f — положение самого символа операции относительно аргументов. Таким образом, шаблон yfx для операции - означает, что выражение X-Y-Z понимается как (X-Y)-Z, шаблон xfy для запятой означает, что t,u,r понимается как t,(u,r),шаблон xfx для :- означает невозможность использования нескольких таких операций подряд без дополнительных скобок. Операции с меньшими приоритетами связывают свои аргументы сильнее. Один и тот же атом может быть определен и как унарная, и как бинарная операция.
Пример описаний операций показывает, что даже локальное использование различения конкретно- и абстрактно-синтаксических представлений программы дает возможность получить большие преимущества. В
Джулией Робинсон доказано (см., напр. ), что для выражений первого порядка имеется эффективный алгоритм
Пример 6.3.1. Две последовательности выражений

где a, b — константы, а латинские буквы из конца алфавита — переменные, унифицируются в

подстановкой

А в двух последовательностях

никакие два соответственных выражения унифицированы быть не могут.
Уже в приведенном примере видно, что
Заметим, что логический алгоритм
Более того, можно было бы унифицировать любые соответственные друг другу внутренние
В первой реализации языка
Желающие в качестве упражнения выловить ошибку самостоятельно, сравните алгоритмы
Рассмотрим, как исполняется программа на языке :- или ?-. В программе, транслируемой и исполняемой в пакетном режиме, обычно используется первый
Исходная цель называется запросом. Переменные, входящие в запрос, носят особый статус. Их значения в ходе последовательных
В каждый момент рассматривается первый из термов цели. Если его детерминатив не является встроенной функцией или встроенным оператором с особым определением, то ищется предложение, голова которого унифицируется с этим термом. При этом прежде всего проверяется наличие предложений, детерминатив которых совпадает с детерминативом первого терма. Если таких предложений несколько, то создается точка
Предложения испытываются, начиная с первого. Полученная унифицирующая подстановка применяется ко всем термам в
Заметим, что переменные предыдущих
Если исполнение оказалось неудачным, то программа возвращается к последней из
Стандартным ответом программы на запрос служит Yes, если программа закончилась удачно, и No, если она закончилась неудачно. При удаче выводятся значения всех переменных исходного запроса.
Так, например, если программа и ее
greater(X,Y):-greater1(X,Y). greater(X,Y):-greater1(Z,Y),greater(X,Z). greater1(X,f(X)). estimation(X,Y):-greater(X,Y),known(Y). known(f(f(f(f(a))))). unknown(a). unknown(b).
то ответом на запрос
?-unknown(Y),estimation(Y,X).
будет
Y=a X=(f(f(f(f(a)))) Yes
а при попытке ответа на запрос
?-estimation(b,X).
программа зациклится.
Насколько каверзны вроде бы невинные предположения (например, условие, что подцели достигаются строго одна за другой и варианты перебираются в том же порядке), сделанные в языке
estimation(X,Y):-known(Y),greater(X,Y).
программа успешно ответит на второй запрос
No
Еще более впечатляющий пример рассмотрен в упражнении 5.
Есть еще одна особенность языка [X|Y] с уже известным списком Z X будет пустым списком), либо зациклится на бесконечном повторении одного и того же варианта (смотри предыдущую скобку, которая иллюстрирует сразу две неприятности: если к такой X ).
Для того, чтобы вычислить выражение, имеется предопределенная бинарная операция is. Она должна иметь вторым аргументом выражение, составленное из атомов при помощи функций. После применения X is 1+2 вместо X подставится 3. Даже выражение 1+2 остается в таком же виде, пока оно не попадет во второй аргумент is.
Внимание!
То, что некоторый функтор определен как операция, не значит, что он вычисляется. Это просто изменение конкретно-синтаксического представления. Для того чтобы иметь возможность вычислить выражение, нужно определить функтор как внутреннюю или внешнюю функцию. При этом необязательно делать его операцией.
Рассмотренные до сих пор средства языка
Пример 6.3.2. Рассмотрим, как с помощью ! и списков программируется поиск пути в лабиринте (и даже в произвольном ориентированном графе).
way(X,X,[X]). way(X,Y,[Y|Z]):-connect(U,Y), nomember(Y,Z),way(X,U,Z). way(X,Y,[Y|Z]):-connect(U,Y), way(X,U,Z). nomember(Y,Z):-member(Y,Z),!,fail. nomember(Y,Z). connect(begin,1). connect(1,begin). connect(1,2). connect(2,3). connect(3,1). connect(3,4). connect(4,end).
В ответ на запрос
?-way(begin,end,X).
программа выдаст
X = [end, 4, 3, 2, 1, begin] Yes
Вместо определения nomember можно написать предложение
way(X,Y,[Y|Z]):-connect(U,Y),not (member(Y,Z)),way(X,U,Z).
Предикат отсечения можно использовать для того, чтобы превратить
Внимание!
Некоторые из русскоязычных учебных пособий по языку
Прагматические соглашения о порядке выполнения действий в программе привели к тому, что если мы запишем в форме языка
A:-A.
и этот
Конечно же, несообразности были использованы и для получения новых эффектов. Рассмотрим следующее определение.
repeat. repeat:-repeat.
Если вставить теперь цель !, то эти подцели будут повторяться вплоть до удачи и их побочные эффекты будут исполняться в цикле.
Приведенное выше определение repeat писать в программах не нужно. В стандарте repeat, потенциально бесконечное число раз успешно унифицируемый. Реализованный в языке
Недетерминированную
Недетерминированным достижением цели называется успешное вычисление. Таким образом, если какая-то из последовательностей продолжений приводит к цели, то цель процесса считается достигнутой. Недетерминированная
Есть теорема, доказывающая, что в принципе недетерминированный конечный автомат всегда можно преобразовать в детерминированный. Идея преобразования — склейка состояний, как показано на рис. 6.1. При этом, содержательно говоря, мы создаем линейный порядок на множестве альтернатив и выбираем альтернативы в
(рис 6.1) Преобразование недетерминированного поиска в детерминированныйТеорема детерминирования конечного автомата обосновывает существование успешного вычисления. Но она не дает никаких хороших оценок изменения сложности вычислений при переходе к детерминированному поиску. И даже если успешное вычисление существует, это не означает, что трансформировать ассоциированные с переходами действия легко и что после трансформации они будут хоть сколько-нибудь понимаемы. По этой причине часто обработку удобнее описывать как недетерминированную, поручая решение задачи организации перебора вариантов системе программирования.
Поскольку структура программы и структура
Для этой цели в . Он читает предложения и факты из файла и помещает их в конец программы, тем самым оставляя в неприкосновенности ранее данные определения предикатов. С его использованием наша программа может быть переписана в следующем виде.
way0(X,Y,Z):-consult(labyr),way(X,Y,Z). way(X,X,[X]). way(X,Y,[Y|Z]):-connect(U,Y), not member(Y,Z),way(X,U,Z). way(X,Y,[Y|Z]):-connect(U,Y), way(X,U,Z).
Пример файла labyr.pl:
connect(begin,1). connect(1,begin). connect(1,2). connect(2,3). connect(3,1). connect(3,4). connect(4,end).
Программа 6.4.1 представляет лишь идею решения, но эту идею она представляет исключительно выразительно.
Есть еще один класс is. К ним, в частности, относятся многие действия над списками. Рассмотрим, например, предикат append(E1,E2,E3). Он корректно унифицируется, когда объединение первых двух списков является третьим. Соответственно, он может использоваться для вычисления любого из своих трех аргументов, если два других заданы. Например,
append(X,Y,Z)
при Z=[a,b,c,d], Y=[c,d] унифицируется как X=[a,b].
Очень жаль, что в
Для динамического порождения фактов и предложений имеются функции, разбирающие предложения и синтезирующие их.
Метапредикат помещает свой аргумент в , наоборот, удаляет из программы предложение или факт, унифицируемый с его аргументом.
С их помощью можно, в частности, имитировать различные более эффективные алгоритмы перебора для работы с лабиринтом, но при этом программа до некоторой степени теряет ясность структуры и становится крайне трудно ее отладить и модифицировать. А эффективности, сравнимой с традиционными методами, достичь все равно не удастся.
Но, например, если Вы анализируете сложную систему правил и ищете вывод, то результат анализа часто можно записать как файл динамически порожденных предложений, и это, наоборот, делает программу красивее, а ее отладку легче. Так что есть смысл использовать динамическое порождение в том случае, когда программа сначала коллекционирует и анализирует информацию, а лишь затем начинает действовать. А порождение, перемешанное с действиями, — кратчайший путь к провалу программы, и должно рассматриваться как хакерство.
Далее, после того, как использованы динамически порожденные факты или предложения, их можно удалить из программы при помощи предиката
retractall(Name / Arity)
Этот предикат удаляет все предложения и факты, говорящие о предикате Name
Внимание!
В новых версиях языка dynamic(connect).
Для проверки типов термов имеются, в частности, следующие встроенные предикаты.
var(Term). Унифицируется, если Term nonvar(Term). Унифицируется, если textsfTerm не integer(Term) Успешен, если Term является целым числом (именно числом, а не выражением).float(Term) Успешен, если Term является действительным числом.number(Term) Успешен, если Term является числом.atom(Term) Успешен, если Term является атомом.string(Term). Успешен, если Term является строкой.atomic(Term). Успешен, если Term является неделимым значением (число, строка или атом).compound (Term). Успешен, если Term является сложным выражением.ground (Term). Успешен, если Term не содержит Для анализа и построения термов имеются, в частности, следующие предикаты.
functor (Term, Functor , Arity ) Унифицируется, если Term является термом с главным Functor Arity . Term, являющийся переменной, унифицируется с новой переменной. Если Term является атомом либо числом, то его arg(Arg, Term, Value) Выделение аргумента терма Term по его номеру Arg.Номера начинаются с 1. Естественно, что данный предикат может быть использован и для определения номера аргумента в терме.Term =.. List Унифицируется, если List является списком, головой которого является Term, а оставшиеся члены задают аргументы (сравните с тем, что ниже рассматривается в языке LISP!) Если не использовать его как операцию, то имя этого предиката Univ. Естественно, он может работать в обе стороны, разбирая либо собирая терм.Примеры.
?- send(hello, X) =.. List. List = [send, hello, X] ?- Term=.. [send, hello, X] Term = send(hello,X)
free_variables(Term, List) List унифицируется как список новых переменных, каждая из которых равна свободной терма Term.
atom_codes(Atom, String) Преобразование атома в строку и наоборот.
Многие из реализаций языка
Во многих случаях даже в поисковой программе необходимо производить вычисления. В языке 1 + 1 + 1, но оно не будет равно 3.
Для организации вычисления имеется специальное отношение X is E. В этом отношении Е является таким выражением, которое после подстановки текущих значений переменных Х унифицируется как ее значение.
Таким образом, можно постепенно накапливать вычисления, а затем в подходящий момент их произвести. Смотрите пример.
?- assert(a(1+1)). Yes ?- assert(b(2 * 2)). Yes ?- a(X), b(Y), Z is X + Y. X = 1+1 Y = 2*2 Z = 6
Внимание!
is не является присваиванием! Для того, чтобы убедиться в этом, исполните простейшее предложение языка
?- X is 1, X is X + 1.
Лучше всего и естественней всего вводятся в .
Конечно же, имеется и более традиционная система ввода-вывода. Опишем ее базовые возможности.
open(SrcDest, Mode, Stream, Options)
Открытие файла. SrcDes является атомом, содержащим имя файла в обозначениях системы Unix. Mode может быть read, write, append или update. Два последних способа открытия используются, соответственно, для дописывания в существующий файл и для частичного переписывания его. Stream либо переменная, и тогда ей присваивается целое число, которое служит для идентификации файла, либо атом, и тогда он служит внутри программы именем файла. Options могут быть опущены, среди них нам важна одна опция: type(binary), которая позволяет записать коды в двоичный файл. Опции образуют список.
Конечно же, имеется возможность вручную установить текущую позицию внутри файла:
seek(Stream, Offset, Method, NewLocation)
Method — это метод отсчета относительной позиции. отсчитывает ее с начала файла, current от нынешней точки, eof от конца. Переменная NewLocation унифицируется с новой позицией, отсчитываемой обязательно с начала.
Предикат close(Stream) комментариев не требует.
read(Stream, Term)
Переменная Term унифицируется с термом, прочитанным из потока Stream.
read_clause(Stream, Term)
Читается предложение. По умолчанию пользователя предупреждают о переменных, которые отсутствуют в голове и лишь однажды присутствуют в хвосте.
read_term(Stream, Term, Options)
Аналогично read, но позволяет установить целый ряд возможностей, регулирующих представление терма. Смотрите подробнее в документации конкретной
writeq(Stream, Term)
Term пишется в Stream, вставляются кавычки и скобки, где нужно.
write_canonical(Stream, Term)
Term пишется в Stream таким способом, чтобы его однозначно прочитала любая
Есть способ читать и писать символы, а через них строки и прочее, но это настолько примитивно и уродливо, что можно дать практический совет:
Внимание!
Если Вам нужно ввести в
Тем не менее вот минимальный (и практически полный) список предикатов символьного и двоичного ввода и вывода.
get_byte(Stream, Byte)
Byte рассматривается как целое число и унифицируется со следующим байтом входного потока. Конец файла читается как -1.
get_char(Stream, Char)
Аналогично, но следующий байт рассматривается как имя атома, состоящее из одного символа. Конец файла унифицируется с атомом end_of_file. Русские буквы, пробелы и прочие нестандартные символы могут вызвать неприятности.
get(Stream, Char)
Аналогично, но пропускаются невидимые символы.
skip(Stream, Char)
Пропускает все, пока не встретится символ Char либо конец файла. Само первое вхождение Char также будет пропущено.
put(Stream, Char)
Вывод одного символа либо байта. Char унифицируется либо как целое число из диапазона [0, 255], либо как атом с именем из одного символа.
nl(+Stream)
Вывести перевод строки.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.