Prolog
День добрый. Есть необходимость использования языка prolog (нет, зачетная сессия еще не началась 🙂 ), сам язык потихоньку осваиваю по книге И.Братко «Программирование на языке ПРОЛОГ для исскуственного интеллекта», но разобраться в swi-prolog и gnu-prolog никак не могу. Подкиньте документацию к этим компиляторам пожалуйста, желательно на русском.И было бы неплохо узнать структуру программ на диалектах для этих компиляторов, может кто подскажет?
Dikar ★★
20.02.10 13:15:53 MSK
← 1 2 →
Об Братко весь мозг сломаешь, для начала попроще бы что-нибудь нашёл. А с swi-prolog’ом раньше нормальная документация шла, на английском, правда. Но я не специалист, с универа пролог не видел. А в чём проявляется «необходимость» использовать именно пролог?
Gvidon ★★★★
( 20.02.10 14:04:37 MSK )
Ответ на: комментарий от Gvidon 20.02.10 14:04:37 MSK
Необходимость обуславливается курсом логического программирования в институте:)
Dikar ★★
( 20.02.10 14:09:20 MSK ) автор топика
Ответ на: комментарий от Dikar 20.02.10 14:09:20 MSK

>Необходимость обуславливается курсом логического программирования в институте:)
исчо раз всем рекомендую, если препод не заинтересован что-то интересное рассказать, то надо сдать как можно быстрее лабы и попробовать досрочно сдать экзамен, на оставшееся время можешь делать, что хочешь.
dimon555 ★★★★★
( 20.02.10 14:24:38 MSK )
Дык к преподу подойди. Он только рад будет увидеть студента, который собирается делать лабы по прологу сам 🙂 И должны же у вас семинары какие-нибудь быть, не на листочке же вы там проги пишете.
Gvidon ★★★★
( 20.02.10 14:26:08 MSK )
Сюрприз! Документация идет в составе самих gprolog и swi-prolog. Так же как и исходники, которые есть лучшая документация. Русский язык не нужен, на нем все равно ничего никогда нет.
lisprocks
( 20.02.10 15:09:35 MSK )
Кстати, лучший способ изучить Пролог — это написать интерпретатор Пролога. Хороший пример был в дистрибутиве Hugs, там сразу два разных коротеньких интерпретатора.
Для более глубокого понимания надо читать про WAM.
lisprocks
( 20.02.10 15:11:31 MSK )
Ответ на: комментарий от Gvidon 20.02.10 14:26:08 MSK
Препод кроме C-Prolog, Turbo Prolog и VisualProlog ничего не использовал, сам попросил притащить SWI-Prolog или GProlog. Лабы мы пишем на Turbo-Prolog, структура программы которого отличается от SWI-Prolog довольно сильно.
Dikar ★★
( 20.02.10 15:32:15 MSK ) автор топика
Ответ на: комментарий от lisprocks 20.02.10 15:09:35 MSK
Полистал офф-мануал с сайта SWI-Prolog, полноценных примеров не нашел, только сама документация. Дайте пруф плиз.
Dikar ★★
( 20.02.10 15:34:38 MSK ) автор топика
Ответ на: комментарий от lisprocks 20.02.10 15:11:31 MSK
Насколько я знаю , книга Братко для пролога столь же монументальна как K&R для Си, вот дочитаю и посмотрим стоит ли дальше изучать этот язык.
Dikar ★★
( 20.02.10 15:37:44 MSK ) автор топика
Ответ на: комментарий от Dikar 20.02.10 15:34:38 MSK
Дожили. Мануалы народ уже на сайтах ищет.
apt-get install swi-prolog (или что там у тебя)
И смотри документацию в установленном. Она в составе дистрибутива идёт. Вместе с примерами. Кроме того, там же есть и исходники, которые лучше всякой документации и лучше синтетических примеров.
lisprocks
( 20.02.10 16:01:36 MSK )
Ответ на: комментарий от Dikar 20.02.10 15:37:44 MSK
Пролог изучать надо обязательно и каждому. Кто не изучил, тот лох. Но вот что совершенно не нужно, так это пользоваться всякими готовыми реализациями конкретно Пролога, потому что это весьма говняненький язычок.
Пролог надо понимать на уровне, достаточном для полноценной реализации его интерпретатора или компилятора. И использовать эти знания при нормальном, кошерном программировании. Большинство систем принятия решений, rule-based systems и тому подобного, что пишется на Java и прочих C#, по сути, является реализациями подмножеств Пролога. И по этому его и необходимо понимать, и на фиг не надо писать на самом Прологе.
lisprocks
( 20.02.10 16:04:04 MSK )
Ответ на: комментарий от lisprocks 20.02.10 16:04:04 MSK
А нафига пролог-то тогда? Ботаешь себе спокойненько матлогику, предикаты там всякие и живёшь счастливо.
Gvidon ★★★★
( 20.02.10 16:07:50 MSK )
Ответ на: комментарий от Gvidon 20.02.10 16:07:50 MSK
> А нафига пролог-то тогда? Ботаешь себе спокойненько матлогику, предикаты там всякие и живёшь счастливо.
Пролог — это и есть логика предикатов первого порядка + алгоритм разрушительной унификации с cut-ом и бектрекингом.
Поймешь реализацию этого алгоритма — поймешь и пролог, и дейталог, и все производные системы. Ничего другого в нем просто нет.
lisprocks
( 20.02.10 16:13:04 MSK )
Ответ на: комментарий от lisprocks 20.02.10 16:13:04 MSK

Как человек, судя по всему разбирающйся в теории, не могли бы вы на пальцах пояснить почему именно дизъюнкты Хорна легли в основу Пролога?
mclaudt ☆
( 20.02.10 16:42:19 MSK )
Ответ на: комментарий от Gvidon 20.02.10 14:04:37 MSK
>Об Братко весь мозг сломаешь, для начала попроще бы что-нибудь нашёл.
Братко как раз пишет на редкость доступно и просто. Очень советую.
А структура программы в SWI Prolog такая же, как в примерах Братко. Без всех этих секций аля Паскаль.
Документация http://www.swi-prolog.org/pldoc/index.html , вверху — справочник на английском. Можешь еще в самом прологе набрать
получив тот же справочник, только в окне справки. И, заодно,
ipc ★
( 20.02.10 16:54:49 MSK )
>но разобраться в swi-prolog и gnu-prolog никак не могу.
Что конкретно вызывает затруднения?
ipc ★
( 20.02.10 16:57:01 MSK )

Про русский забудь.
mclaudt ☆
( 20.02.10 17:11:32 MSK )

Лучший ресурс по русскоязычным учебным материалам
Хоть влать и ругают, но этот проект работает на отлично.
mclaudt ☆
( 20.02.10 17:16:04 MSK )
Ответ на: комментарий от ipc 20.02.10 16:57:01 MSK
конкретно после Turbo Prolog было непонятно хотябы-то как компилировать программу.Было неясно нужны ли пресловутые domainc, constants, goals и прочие секции в начале программы. И т.д.
Dikar ★★
( 20.02.10 17:21:27 MSK ) автор топика
Ответ на: комментарий от mclaudt 20.02.10 17:16:04 MSK
Спасибо, но книжка мне ближе, а лекции и так хорошие и понятные читают.)
Dikar ★★
( 20.02.10 17:22:59 MSK ) автор топика
Ответ на: комментарий от Dikar 20.02.10 17:21:27 MSK
Если только это, то после
думаю, все стало понятно.
ipc ★
( 20.02.10 17:24:57 MSK )
Ответ на: комментарий от Gvidon 20.02.10 16:07:50 MSK
Пролог плюс к тому что нужен в универе, довольно интересен и очень локаничен. При этом на нем удобнее чем на большинстве других языков писать экспертные системы. Последнее я узнал от препода, но после 2 недель изучения языка в этом все больше уверяюсь.
Что за взгляды такие странные? Выучили 2-3 самых популярных языка (читай C,C++,Java) , на большее не хватило терпения, а везде твердят что декларативные языки не нужны. Странные люди.
Dikar ★★
( 20.02.10 17:29:02 MSK ) автор топика
Ответ на: комментарий от ipc 20.02.10 17:24:57 MSK
Dikar ★★
( 20.02.10 17:29:53 MSK ) автор топика
Ответ на: комментарий от ipc 20.02.10 17:24:57 MSK

А как у вас происходило постижение сути Пролога?
Открылась ли структурная простота и обоснованность, или же просто свыклись, по принципу стерпится-слюбится?
Так почему же именно хорновские дизъюнкты-то?
mclaudt ☆
( 20.02.10 17:36:56 MSK )
Ответ на: комментарий от mclaudt 20.02.10 17:36:56 MSK
>А как у вас происходило постижение сути Пролога?
У меня было сугубо учебно-практическим по мере понимания, как всё работает.
ipc ★
( 20.02.10 18:43:00 MSK )
Ответ на: комментарий от Dikar 20.02.10 17:29:02 MSK
> Что за взгляды такие странные? Выучили 2-3 самых популярных языка (читай C,C++,Java) , на большее не хватило терпения, а везде твердят что декларативные языки не нужны. Странные люди.
Я не говорил, что декларативные языки не нужны. Но сфера применения пролога довольно не велика, и прежде, чем на нём писать что-то кроме лаб по нему, неплохо бы подумать, а нужно ли оно тебе.
Gvidon ★★★★
( 20.02.10 22:14:25 MSK )
Ответ на: комментарий от Gvidon 20.02.10 22:14:25 MSK
Изучить парадигму полезно вне зависимости от того будешь ли ты использовать именно её, возможность в будущем посмотреть на задачу с другой стороны возможно будет уже частью решения задачи.
Dikar ★★
( 20.02.10 22:41:26 MSK ) автор топика
Ответ на: комментарий от ipc 20.02.10 17:24:57 MSK
Есть 2 практически одинаковых предиката:
father(X,Y):- roditel(X,Y), man(X). mother(X,Y):- roditel(X,Y), woman(X).
но их вывод разнится:
?- mother(X,k). X = g ; false. ?- father(X,k). X = v.
Почему father пишет просто X=v, а mother еще и какой-то false?
И еще вопрос: как запускать пролог программы прямо из emacs? Нажимаю С-с С-b , от крывается окно с приветствием swi-prolog, но сделать ничего не получается:
For help, use ?- help(Topic). or ?- apropos(Word). ?- |: |: help(dif). |: father(X,k). Warning: user://1:12: Singleton variables: [X] |: ?-father(X,k). Warning: user://1:15: Singleton variables: [X] |:
Dikar ★★
( 20.02.10 23:48:47 MSK ) автор топика
Ответ на: комментарий от Dikar 20.02.10 23:48:47 MSK

>>Почему father пишет просто X=v, а mother еще и какой-то false?
Там если после первого вывода нажать ; то она продолжит искать и порой натыкается на false, а если Enter то остановится на достигнутом.
mclaudt ☆
( 21.02.10 00:04:45 MSK )
Ответ на: комментарий от Dikar 20.02.10 23:48:47 MSK
>Почему father пишет просто X=v, а mother еще и какой-то false?
На самом деле никакой разницы здесь нет.
Вначале ты вводишь
Здесь тебе предлагается выбрать следующее действие. Можешь попробовать нажать h — получишь справку по тому, что можешь вводить.
Ввод «.» — завершить поиск подходящих значений.
Ввод «;» — продолжить.
Поскольку другого значения нет, получаешь false.
И еще вопрос: как запускать пролог программы прямо из emacs?
C-c C-b и вводишь свои _запросы к Прологу_ в консоли. Объяви какой-нибудь «go», и вызывать его чтобы меньше писать. Может, есть какое-то предопределенное имя для цели по-умолчанию, не разбирался. В GNU Prolog кстати, есть, и бинарник можно даже получить.
Singleton variable он пишет оттого, что переменная X в твоем коде больше не используется. Код и консоль Пролога — две разные вещи. Во втором случае оболочка сама выводит тебе значения и т. п.
ipc ★
( 21.02.10 00:13:16 MSK )
Ответ на: комментарий от mclaudt 21.02.10 00:04:45 MSK

Но вот понять почему в одних случаях она выводит false а в других нет — это в доках пока не нашел.
Видать, как-то связано в ветвлением, если она после нажатия клавиши ; она не попробовала ни одной подстановки то ничего не выводится (как и в случае с Enter), а если попробовала, но неудачно, то fail.
(в swi это вроде называется fail а не false)
mclaudt ☆
( 21.02.10 00:14:58 MSK )
Ответ на: комментарий от mclaudt 21.02.10 00:14:58 MSK
fail — это искусственно вызвать false.
test1 :- write(«blablabla»), fail.
ipc ★
( 21.02.10 00:18:22 MSK )
Ответ на: комментарий от ipc 21.02.10 00:18:22 MSK

?- test1. [98, 108, 97, 98, 108, 97, 98, 108, 97] fail.
SWI-Prolog version 5.6.54 for x86_64-linux-gnu
Блин наплодили диалектов.
mclaudt ☆
( 21.02.10 00:28:28 MSK )
Ответ на: комментарий от mclaudt 21.02.10 00:28:28 MSK
test1 :- write(‘blablabla’), fail.
?- test1.
blablabla
false.
SWI-Prolog version 5.6.64 for i686-linux
ipc ★
( 21.02.10 00:32:38 MSK )
Ответ на: комментарий от ipc 21.02.10 00:32:38 MSK

У меня все то же только вместо false стоит fail.
Разработчики опять на ровном месте всех запутали, ни хрена не продкуоментировав эпохальные изменения.
mclaudt ☆
( 21.02.10 00:41:12 MSK )
Ответ на: комментарий от mclaudt 21.02.10 00:14:58 MSK
>Видать, как-то связано в ветвлением, если она после нажатия клавиши ; она не попробовала ни одной подстановки то ничего не выводится (как и в случае с Enter), а если попробовала, но неудачно, то fail.
Почти верно. Это связано с точками возврата: они или остаются после очередного успешного доказательства, или нет. При нажатии «;» происходит откат к последней такой точке (прямо как если бы в этом месте стоял false) и процесс доказательства начинается с того места по альтернативному пути. Это доказательство может обломаться и тогда получим ‘false.’ на экране. Ну а если точек не осталось, то и спрашивать как-бы не о чем — процесс доказательства окончен (иногда в конце пишется ‘true.’).
Чтобы понять, чем предикат false отличается от fail, надо попробовать выполнить listing(false).
anonymous
( 21.02.10 04:10:45 MSK )
Ответ на: комментарий от mclaudt 21.02.10 00:41:12 MSK
Если тебя смущает напечатанный список символов вместо собственно текста, то дело не в false/fail, конечно же, а в кавычках. Одиночные кавычки — это атомы, двойные — строки (которые суть то же самое, что и списки символов).
anonymous
( 21.02.10 04:19:05 MSK )
Ответ на: комментарий от anonymous 21.02.10 04:10:45 MSK

Спасибо, так и понимал.
Чтобы понять, чем предикат false отличается от fail, надо попробовать выполнить listing(false).
Нет такого. Вопреки всей логике, главную пару констант образуют не true и false, а true и fail. False даже нету в документации.
mclaudt ☆
( 21.02.10 04:30:18 MSK )
Ответ на: комментарий от anonymous 21.02.10 04:19:05 MSK

Ага,с кавычками-то ясно — это не вопрос.
Короче, при переходе от 5.6.54 к 5.6.64 они поменяли true/fail на true/false.
mclaudt ☆
( 21.02.10 04:35:51 MSK )
Ответ на: комментарий от mclaudt 21.02.10 04:30:18 MSK
>Вопреки всей логике, главную пару констант образуют не true и false, а true и fail. False даже нету в документации. Если речь о предикатах, то не «вопреки логике», а «согласно iso стандарту», там прописан именно fail. false определен в swi тривиальным образом как (false :- fail). Когда его туда ввели я не обратил внимания, у меня сейчас 5.8.0 стоит и он там есть. А если речь о том, что там печатается на экране, то это дело десятое. В YAP, например, вообще ‘yes/no’.
anonymous
( 21.02.10 04:48:10 MSK )
Ответ на: комментарий от anonymous 21.02.10 04:48:10 MSK

«согласно iso стандарту», там прописан именно fail. false определен в swi тривиальным образом как (false :- fail).
Тут вспомнил ещё одну «фичу»— обидно что не работает подборка списков некольких вложенностей.
?- flatten(X,[a,b,c]). fail.
А так хочется чтоб она вернула все возможные [[a,b],c], [[[a]],,c] …
Понятно что комбинаторный взрыв но ведь можно установить предельную глубину.
mclaudt ☆
( 21.02.10 05:16:44 MSK )
Ответ на: комментарий от mclaudt 20.02.10 17:36:56 MSK
Так почему же именно хорновские дизъюнкты-то?
Потому что резолютивное опровержение на хорновских КНФ имеет гарантированно полиномиальную сложность. А чем тебя так беспокоит «хорновскость» пролога?
twosev ★★
( 21.02.10 08:30:34 MSK )
Ответ на: комментарий от twosev 21.02.10 08:30:34 MSK

>>Потому что резолютивное опровержение на хорновских КНФ имеет гарантированно полиномиальную сложность.
А зачем ограничиваться резолютивным опровержением? Если все сводится к причесыванию НФ, так существует куча методов их упрощения, и если не ограничиваться одним методом то причесать можно оптимальнее? (Общая задача останется полиномиальной, не спорю)
А чем тебя так беспокоит «хорновскость» пролога?
Что-то мне подсказывает что благодаря двойственности конъюнкции и дизъюнкции должна существовать подобная система, основанная на другой операции.
Метод резолюций — это всего лишь вычёсывание катышков-«тавтологий» вида A⋁¬A из некой промежуточной дизъюнктивной формы.
Должен тогда существовать метод, основанный на вычёсывании катышков-«противоречий» вида A⋀¬A из некой промежуточной коньюнктивной формы.
mclaudt ☆
( 21.02.10 15:22:07 MSK )
Ответ на: комментарий от mclaudt 21.02.10 15:22:07 MSK

Хотя, наверное, с точностью до переобозначения true и false это будет один и тот же метод.
mclaudt ☆
( 21.02.10 15:24:17 MSK )
Ответ на: комментарий от mclaudt 21.02.10 15:22:07 MSK
> Если все сводится к причесыванию НФ.
Метод резолюций — это всего лишь вычёсывание катышков-«тавтологий».
Метод резолюций — это метод логического вывода, использующий правило резолюций (и только). К причесыванию НФ здесь ничего не сводится. Каков вообще смысл резолютивного вывода и принцип работы пролога?
Ты ставишь перед прологом задачу проверки истинности определенного утверждения в некоторой системе аксиом. Он решает эту задачу при помощи метода резолюций (фактически, методом от противного).
Грубо говоря, происходит следующее. Предложениями вида «D :- A, B, C.» ты задаешь истинные утверждения «A & B & C -> D», что на нормальном языке можно записать как «¬A V ¬B V ¬C V D» (хорновский дизъюнкт). Определяя таким образом A, B и т. д., ты формируешь КНФ, составленную только из дизъюнктов указанного вида. Затем к КНФ приписываются все посылки и отрицание логического следствия (идеология метода доказательства от противного), которое необходимо проверить. Таким образом, получается КНФ, составленная только из хорновских дизъюнктов. Если пролог методом резолюций по данной КНФ далее выводит пустой список дизъюнктов, то этот факт означает false. В противном случае, имеем true. Вот и всё (пример, конечно, прост, и объяснения касаются пропозициональной логики — в первопорядковой логике всё сложнее, однако смысл вывода тот же).
При этом нетрудно показать, что на хорновских КНФ резолютивный вывод полиномиален от длины входных данных. Однако никаких полиномиальных оценок для метода резолюций на КНФ, заданных в общем виде, нет и не может быть в предположении, что P != NP (в первопорядковой логике вообще ничего нельзя сказать).
Изначальное ограничение «хорновости» пролога (я не знаю, как сейчас обстоят дела) состоит лишь в невозможности задать предложения вида «D, E, F :- A, B, C.» (то есть в одном предложении допустимо не более одного следствия). Ну, и как уже должно быть понятно из всего вышесказанного, данное ограничение обусловлено эффективностью метода резолюций на данном классе КНФ.
twosev ★★
( 21.02.10 17:29:29 MSK )
Ответ на: комментарий от ipc 21.02.10 00:13:16 MSK
>C-c C-b и вводишь свои _запросы к Прологу_ в консоли. Объяви какой-нибудь «go», и вызывать его чтобы меньше писать. Может, есть какое-то предопределенное имя для цели по-умолчанию, не разбирался.
Разве то что вводил — не запросы к Прологу?
можно хотя-бы пример подобного запроса?
А то везде как-то работает «roditel(X,k).» (В консоли после swipl /path/to/file работает, в Turbo-prolog — работает, в GNU Prolog — работает), а в emacs — нет.
Dikar ★★
( 21.02.10 19:25:59 MSK ) автор топика
Ответ на: комментарий от Dikar 21.02.10 19:25:59 MSK
>можно хотя-бы пример подобного запроса?
в emacs:
test2 :- a(X), write(X), nl, fail.
Запрос в консоли:
?- a(X).
X = 1 ;
X = 2 ;
X = 3 ;
true.
А то везде как-то работает «roditel(X,k).» (В консоли после swipl /path/to/file работает, в Turbo-prolog — работает, в GNU Prolog — работает), а в emacs — нет.
Ну и я пишу, что в консоли это работает, а в файле(в том числе, редактируемом в emacs) нет. Или в чем вопрос?
localhost$ pl -f test.pl -g test2
% test.pl compiled 0.00 sec, 1,456 bytes
1
2
3
[49]
% halt
ipc ★
( 22.02.10 00:20:37 MSK )
Ответ на: комментарий от ipc 22.02.10 00:20:37 MSK
Проблема с emacs решена, точнее она решилась сама собой: после приветствия появились обычные консольные запросы (?-) а не то что было (?- 😐 😐 ).
Если не трудно разъясните смысл строки
test2 :- a(X), write(X), nl, fail.
a(X) и write(X) понятны, но зачем некий nl и fail ?
Dikar ★★
( 22.02.10 01:46:38 MSK ) автор топика
Ответ на: комментарий от Dikar 22.02.10 01:46:38 MSK
с nl разобрался — стоило залезть в мануал, а fail то зачем?
Dikar ★★
( 22.02.10 02:00:16 MSK ) автор топика
Ответ на: комментарий от Dikar 22.02.10 01:46:38 MSK

>>но зачем некий nl и fail ?
На такие вещи лучше учиться отвечать самому.
nl — new line, не имеющая отношения к работе пролога приблуда для выдачи новой строки.
fail объясняли выше.
mclaudt ☆
( 22.02.10 02:05:43 MSK )
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.
← 1 2 →
Похожие темы
- Форум Логическое программирование (2005)
- Форум Prolog (2004)
- Форум [Prolog] Объясните. (2010)
- Форум Prolog implementation. (2008)
- Форум Prolog + Book (2009)
- Форум STM32 с нуля (жж) (2023)
- Форум бэкенд на prolog (2019)
- Форум rdf vs prolog (2011)
- Форум Prolog success story. (2008)
- Форум Prolog на Буране (2012)
Логическое программирование
Предикаты для работы с утверждениями
Пролог позволяет программисту анализировать и изменять программу во время выполнения.
Предикат consult/1 предназначен для добавления утверждений из некоторого файла к утверждениям, уже имеющимся в БД. Аргумент предиката должен быть атомом, содержащим имя файла, например, 'w:/flp/lab1.pl', или списком имен файлов. Вводимые утверждения заменяют все имеющиеся утверждения для того же самого предиката.
Предикат listing позволяет вывести утверждения из БД в текущий выходной файл. Цель listing выводит все утверждения из БД, цель listing(N/A) – все утверждения для предиката с именем N и арностью A, а цель listing(N) – утверждения для всех предикатов с именем N любой арности. Используя предикат listing, можно сохранять состояние БД перед завершением работы программы, а в начале работы загружать информацию с помощью предиката consult.
Предикат clause/2 полезен при создании программ, анализирующих или обрабатывающих другие программы. Цель clause(X,Y) сопоставляет термы X и Y с головой и телом некоторого имеющегося в БД утверждения. Терм X должен быть конкретизирован до такой степени, чтобы стал известен функтор структуры. Если утверждение является фактом, то Y сопоставляется с true. Если имеется несколько утверждений, соответствующих термам X и Y, то Пролог выбирает первое из них. В случае возврата будет выбрано следующее утверждение и так далее. Предикат в GNU Prolog должен быть объявлен в программе с помощью команд :-public(N/A) или :-dynamic(N/A).
Предикаты asserta/1 и assertz/1 используются для добавления новых утверждений в БД. Предикат asserta(X) добавляет утверждение X в начало БД, а assertz(X) – в конец. При добавлении правил аргумент предиката нужно ставить в дополнительные скобки: assertz((a:-b,c)). Предикат asserta можно использовать для повышения эффективности программы, запоминая результаты предыдущих вычислений, чтобы не вычислять предикат повторно для тех же самых аргументов.
Предикат retract(X) удаляет из БД первое утверждение, сопоставимое с термом X. При возврате выполняется поиск и удаление следующего утверждения, сопоставимого с X. Если таких утверждений нет, то согласование цели завершается неудачей. Так как при удалении происходит сопоставление терма и утверждения, то имеется возможность получить точное представление об удаляемом утверждении. Можно определить предикат, удаляющий все утверждения с заданной головой.
retractall(X):-retract(X),fail. retractall(X):-retract((X:-Y)),fail. retractall(X).
В этом определении предикат fail обеспечивает обработку всех утверждений предикатом retract.
Предикаты assertz и retract позволяют создать аналог глобальных переменных.
:-dynamic(mem/2). % mem изменяется во время выполнения set(X,V):-(retract(mem(X,_));true),assertz(mem(X,V)). ?-set(a,10). ?-mem(a,X). X=10
Но использование таких побочных эффектов влияет на логическую обоснованность программы, так как отношения, выполнявшиеся в некоторый момент времени, могут оказаться неверными в другой. Обычно предикаты asserta/z и retract используются для изменения БД в соответствии с информацией, полученной от пользователя, а затем на основе этих знаний выполняется логический вывод.
Упражнения
1. Пусть имеется набор фактов вида:
словарь(like,нравится). словарь(fish,рыба).
Определите предикат, выполняющий перевод предложений с английского языка на русский. В случае неизвестного слова необходимо делать запрос его значения у пользователя и добавлять новое определение в словарь.
?-перевод([cat,like,fish],R). Введите перевод для слова cat: кот. R=[кот,нравится,рыба]
2. Используя сохранение промежуточных результатов в специальном факте, определите предикат дети(X,L) для получения списка детей X для БД о родственных связях из главы 1.
?-дети(адам,R). R=[каин,авель]
3. Определите предикат := для вычисления арифметических вычислений, запоминающий значения вычисленных переменных (обозначаемых атомом Пролога) и печатающий результат.
?-a:=1. a=1 ?-b:=a*3+5. b=8
Логическое программирование на Prolog для чайников

Меня зовут Михаил Горохов, и эта статья написана в рамках вузовского курсового проекта. В первую очередь эта статья ориентирована на студентов ПМ и ПМИ , на долю которых выпало изучение прекрасного мощного языка Prolog, и которым приходится изучать совершенно новый и непривычный для них подход. Первое время он даже голову ломает. Особенно любителям оптимизации.
В конце статьи я оставлю полезные ссылки. Если у вас останутся вопросы — пишите в комментариях!
Начнем туториал: Пролог для чайников!
Логическое программирование
Существуют разные подходы к программированию. Часто выделяют такие парадигмы программирования:
- Императивное (оно же алгоритмическое или процедурное). Самая известная парадигма. Программист четко прописывает последовательность команд, которые должен выполнить процессор. Примеры: C/C++, Java, C#, Python, Golang, машина Тюрьнга, алгоритмы Маркова. Все четко, последовательно
как надо. Синоним императивного — приказательное. - Аппликативное (Функциональное). Менее известная, но тоже широко используемая. Примеры языков: Haskell, F#, Лисп. Основывается на математической абстракции лямбда вычислениях. Благодаря чистым функциям очень удобно параллелить такие программы. Чистые функции — функции без побочных эффектов. Если такой функции передавать одни и те же аргументы, то результат всегда будет один и тот же. Такие языки обладают высокой надежностью кода.
- И наконец — Декларативное (Логическое). Основывается на автоматическом доказательстве теорем на основе фактов и правил. Примеры языков: Prolog и его диалекты, Mercury. В таких языках мы описываем пространство состояний, в которых сам язык ищет решение к задаче. Мы просто даем ему правила, факты, а потом говорим, что «сочини все возможные стихи из этих слов», «реши логическую задачу», «найди всех братьев, сестер, золовок, свояков в генеалогическом древе», или «раскрась граф наименьшим кол-вом цветов так, что смежные ребра были разного цвета». Что такое ЛП я обозначил. Предлагаю сразу перейти к практике, к основам Prolog (PROgramming in LOGic). На практике все становится понятнее. Практику и теорию я буду чередовать. Не беспокойтесь, если сразу будет что-то не понятно. Повторяйте за мной, и вы разберетесь.
Установка Prolog
Существую разные реализации (имплементации) Пролога: SWI Prolog, Visual Prolog, GNU Prolog. Мы установим SWI Prolog.
Установка на Arch Linux:
sudo pacman -S swi-prolog
Установка на Ubuntu:
sudo apt install swi-prolog
Prolog работает в режиме интерпретатора. Теперь можем запустить SWI Prolog. Запускать не через swi-prolog, а через swipl:
[user@Raft ~]$ swipl Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.3) SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software. Please run ?- license. for legal details. For online help and background, visit https://www.swi-prolog.org For built-in help, use ?- help(Topic). or ?- apropos(Word). ?-
Ура! Оно работает!
Теперь поставим на Windows.
Перейдем на официальный сайт на страницу скачивания стабильной версии. Ссылка на скачивание. Клик. Скачаем 64х битную версию. Установка стандартная. Чтобы ничего не сломать, я решил галочки не снимать. Ради приличия я оставлю скрины установки.




Основы Prolog. Факты, правила, предикаты
Есть утверждения, предикаты:
- Марк изучает книгу (учебник, документацию)
- Маша видит клавиатуру (мышку, книгу, тетрадь, Марка)
- Миша изучает математику (ЛП, документацию, учебник)
- Саша старше Лёши
С английского «predicate» означает «логическое утверждение».
Есть объекты: книга, клавиатура, мышка, учебник, документация, тетрадь, математика, ЛП, Марк, Маша, Саша, Даша, Лёша, Миша, да что угодно может быть объектом.
Есть отношения между объектами, т.е то, что связывает объекты. Связь объектов можно выразить через глаголы, например: читать, видеть, изучать. Связь можно выразить через прилагательное. Миша старше Даши. Даша старше Лёши. Получается.. связью может быть любая часть речь? Получается так.
Прекрасно! Давайте попробуем запрограммировать эти утверждения на Прологе. Для этого нам нужно:
- Создать новый текстовый файл, который я назову simple.pl (.pl — расширение Пролога)
- В нем написать простой однострочный код на Прологе
- Запустить код с помощью SWI Prolog
- Спросить у Пролога этот факт
study(mark, book).
Запустим. На линуксе это делается таким образом:
[user@Raft referat]$ swipl simple.pl Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.3) SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software. Please run ?- license. for legal details. For online help and background, visit https://www.swi-prolog.org For built-in help, use ?- help(Topic). or ?- apropos(Word). ?-
На Windows я использую notepad++ для написания кода на Прологе. Я запущу SWI-Prolog и открою файл через consult.

Что мы сделали? Мы загрузили базу знаний (те, которые мы описали в простом однострочном файле simple.pl) и теперь можем задавать вопросы Прологу. То есть система такая: пишем знания в файле, загружаем эти знания в SWI Prolog и задаем вопросы интерпретатору. Так мы будем решать поставленную задачу. (Даже видно, в начале интерпретатор пишет «?- «. Это означает, что он ждет нашего вопроса, как великий мистик)
Давайте спросим «Марк изучает книгу?» На Прологе это выглядит так:
?- study(mark, book). true. ?-
По сути мы спросили «есть ли факт study(mark, study) в твоей базе?», на что нам Пролог лаконично ответил «true.» и продолжает ждать следующего вопроса. А давайте спросим, «изучает ли Марк документацию?»
?- study(mark, book). true. ?- study(mark, docs). false. ?-
Интерпретатор сказал «false.». Это означает, что он не нашел этот факт в своей базе фактов.
Расширим базу фактов. После я определю более строгую терминологию и опишу, что происходит в этом коде.
Сделаю важное замечание для начинающих. Сложность Пролога состоит в специфичной терминологии и в непривычном синтаксисе, в отсутствии императивных фич, вроде привычного присвоения переменных.
% Код на прологе. Я описал 11 фактов. % Каждый факт оканчивается точкой ".", как в русском языке, как любое утверждение. % Комментарии начинаются с "%". % Интерпретатор пролога игнорирует такие комментарии. /* А это многострочный комментарий */ % Факты. 11 штук study(mark, book). % Марк изучает книгу study(mark, studentbook). % Марк изучает учебник study(mark, docs). % Марк изучает доки see(masha, mouse). % Маша видит мышь see(masha, book). % Маша видит книгу see(masha, notebook). % Маша видит тетрадь see(masha, mark). % Маша видит Марка study(misha, math). % Миша изучает матешу study(misha, lp). % Миша изучает пролог study(misha, docs). % Миша изучает доки study(misha, studentbook). % Миша изучает учебник
Терминология. Объекты данных в Прологе называются термами (предполагаю, от слова «термин»). Термы бывают следующих видов:
- Константами. Делятся на числа и атомы. Начинаются с маленькой буквы. Числа: 1,36, 0, -1, 123.4, 0.23E-5. Атомы — это просто символы и строки: a, abc, neOdinSimvol, sTROKa. Если атом состоит из пробела, запятых и тд, то нужно их обрамлять в одинарные кавычки. Пример атома: ‘строка с пробелами, запятыми. Eto kirilicca’.
- Переменными. Начинаются с заглавной буквы: X, Y, Z, Peremennaya, Var.
- Структурами (сложные термы). Например, study(misha, lp).
- Списками. Пример: [X1], [Head|Tail]. Мы разберем их позже в этой статье. Есть хорошая статья, которая подробно рассказывает про синтаксис и терминологию Пролога. Рекомендую её, чтобы лучше понять понятия Пролог.
Пролог использует методы полного перебора и обход дерева. Во время проверки условий (доказательства утверждений) Пролог заменяет переменные на конкретные значения. Через пару абзацев будут примеры.
study(mark, book). — такие конструкции называются фактами. Они всегда истинны. Если факта в базе знаний нету, то такой факт ложный. Факты нужно оканчивать точкой, так же как утверждения в русском языке.
«Задавать вопросы Прологу» означает попросить Пролог доказать наше утверждение. Пример: ?- study(mark, book). Если наше утверждение всегда истинно, то Пролог напечатает true, если всегда ложно, то false. Если наше утверждение верно при некоторых значениях переменных, то Пролог выведет значения переменных, при которых наше утверждение верно.
Давайте загрузим факты в Пролог и будем задавать вопросы. Давайте узнаем, что изучал mark. Для этого нам нужно написать «study(mark, X).» Если мы прожмем «Enter«, то Пролог нам выдаст первое попавшееся решение
?- study(mark, X). X = book .
Чтобы получить все возможные решения, нужно прожимать точку с запятой «;«.
?- study(mark, X). X = book ; X = studentbook ; X = docs.
Можем узнать, кто изучал документацию.
?- study(Who, docs). Who = mark ; Who = misha.
Можно узнать, кто и что изучал!
?- study(Who, Object). Who = mark, Object = book ; Who = mark, Object = studentbook ; Who = mark, Object = docs ; Who = misha, Object = math ; Who = misha, Object = lp ; Who = misha, Object = docs ; Who = misha, Object = studentbook.
Пролог проходится по всей базе фактов и находит все такие переменные Who и Object, что предикат study(Who, Object) будет истинным. Пролог перебирает факты и заменяет переменные на конкретные значения. Пролог выведет такие значения переменных, при которых утверждения будут истинными. У нас задача состояла только из фактов, и решение получилось очевидным.
Переменная Who перебирается среди имен mark, misha, а переменная Object среди book, studentbook, docs, lp, math.
Who не может равняться masha, потому что masha ничего не узнала согласно нашей базе фактов. Аналогично Object не может равняться tomuChevoNetuVBaze, так как такого значения не было в базе фактов. Для study на втором месте были только book, studentbook, docs, lp, math.
Короче, я старался понятным языком объяснить метод полного перебора, и что Пролог тупо все перебирает, пока что-то не подойдет. Все просто.
А теперь разберем правила в Прологе. Напишем ещё одну программу old.pl.
% Это факты older(sasha, lesha). % Саша старше Лёши older(misha, sasha). % Миша старше Саши older(misha, dasha). % Миша старше Даши older(masha, misha). % Маша старше Миши % Это правило older(X,Y) :- older(X, Z), older(Z,Y). % X старше Y, если X старше Z и Z старше Y % Проще: X > Y, если X > Z и Z > Y % % X, Y, Z - это переменные. % Вместо X, Y, Z подставляются конкретные значения: misha, dasha, sasha, lesha % Main idea: если Пролог найдет среднего Z, который между X и Y, то X старше Y.
older(X,Y) :- older(X, Z), older(Z,Y) — такие конструкции называются правилами. Чтобы из факта получить правило, нужно заменить точку «.» на двоеточие дефис «:-» и написать условие, когда правило будет истинным. Правила истинны только при определенных условиях. Например, это правило будет истинно в случае, когда факты older(X,Z) и older(Z,Y) истинны. Если переформулировать, то получается «X старше Y, если X старше Z и Z старше Y». Если математически: «X > Y, если X > Z и Z > Y».
Запятая «,» в Прологе играет роль логического «И». Пример: «0 < X, X < 5". X меньше 5 И больше 0.
Точка с запятой «;» играет роль логического «ИЛИ». «X < 0; X >5″. X меньше 0 ИЛИ больше 5.
Отрицание «not(Какой-нибудь предикат)» играет роль логического «НЕ». «not(X==5)». X НЕ равен 5.
Факты и правила образуют утверждения, предикаты. (хорошая статья про предикаты)
Сперва закомментируйте правило и поспрашивайте Пролог, кто старше кого.
?- older(masha, X). X = misha.
Маша старше Миши. Пролог просто прошелся по фактам и нашел единственное верный факт. Но.. мы хотели узнать «Кого старше Маша?». Логично же, что если Миша старше Саши И Маша старше Миши, то Маша также старше Саши. И Пролог должен решать такие логические задачи. Поэтому нужно добавить правило older(X,Y) :- older(X, Z), older(Z,Y).
Повторим вопрос.
?- older(masha, X). X = misha ; X = sasha ; X = dasha ; X = lesha ; ERROR: Stack limit (1.0Gb) exceeded ERROR: Stack sizes: local: 1.0Gb, global: 21Kb, trail: 1Kb ERROR: Stack depth: 12,200,525, last-call: 0%, Choice points: 6 ERROR: Probable infinite recursion (cycle): ERROR: [12,200,525] user:older(lesha, _5658) ERROR: [12,200,524] user:older(lesha, _5678) ?-
Программа смогла найти все решения. Но что это такое? Ошибка! Стек переполнен! Как вы думаете, с чем это может быть связано? Попробуйте подумать, почему это происходит. Хорошее упражнение — расписать на бумаге алгоритм older(masha,X) так, как будто вы — Пролог. Видите причину ошибки?
Это связано с бесконечной рекурсией. Это частая ошибка, которая возникает в программировании, в частности, на Прологе. older(X, Y) вызывает новый предикат older(X,Z), который в свою очередь вызывает следующий предикат older и так далее.
Нужно как-то остановить зацикливание. Если подумать, зачем нам проверять первый предикат «older(X, Z)» через правила? Если не нашел факт, то значит весь предикат older(X, Y) ложный (подумайте, почему).
Нужно объяснить Прологу, что факты и правила нужно проверять во второй части older(Z, Y), а в первой older(X, Y) — только факты
Нужно объяснить Прологу, что если он в первый раз не смог найти нужный факт, то ему не нужно приступать к правилу. Нам нужно как-то объяснить Прологу, где факт, а где правило.
Это задачу можно решить, добавив к предикатам ещё один аргумент, который будет показывать — это правило или факт.
% Это факты older(sasha, lesha, fact). % Саша старше Лёши older(misha, sasha, fact). % Миша старше Саши older(misha, dasha, fact). % Миша старше Даши older(masha, misha, fact). % Маша старше Миши % Это правило older(X,Y, rule) :- older(X, Z, fact), older(Z,Y, _). % X старше Y, если X старше Z и Z старше Y % Проще: X > Y, если X > Z и Z > Y % % X, Y, Z - это переменные. % Пролог перебирает все возможные X, Y, Z. % Вместо X, Y, Z подставляются misha, dasha, sasha, lesha % Например: Миша старше Лёши, если Миша старше Саши и Саша старше Лёши
Нижнее подчеркивание «_» — это анонимная переменная. Её используют, когда нам не важно, какое значение будет на её месте. Нам важно, чтобы первая часть правила была фактом. А вторая часть может быть любой.
?- older(masha, X, _). X = misha ; X = sasha ; X = dasha ; X = lesha ; false.
Наша программа вывела все верные ответы.
Возможно, возникает вопрос: откуда Пролог знает, что изучает Марк и что Миша старше Даши? Как он понимает такие человеческие понятия? Почему ассоциируется study(mark, math) с фразой «Марк изучает математику»? Почему не с «математика изучает Марка»?. Это наше представление. Мы договорились, что пусть первый терм будет обозначать «субъект», сам предикат «взаимосвязь», а второй терм «объект». Мы могли бы воспринимать по-другому. Это просто договеренность о том, как воспринимать предикаты. Пролог позволяет нам абстрактно описать взаимоотношения между термами.
Напишем предикат для нахождения факториала от N.
factorial(1, 1). factorial(N, F):- N1 is N-1, factorial(N1, F1), F is F1*N. % В Прологе пробелы, табуляция и новые строки работают также, как C/C++. % Главное в конце закончить предикат точкой.

«is» означает присвоить, т.е N1 будет равняться N-1. Присвоение значений переменным Пролога называется унификацией. «is» работает только для чисел. Чтобы можно было присваивать атомы, нужно вместо «is» использовать » full-width «>
Списки в Прологе отличаются от списков в C/C++, Python и других процедурных языков. Здесь список — это либо пустой элемент; либо один элемент, называемый головой, и присоединенный список — хвост. Список — это рекурсивная структура данных с последовательным доступом.
Списки выглядят так: [],[a], [abc, bc], [‘Слово 1’, ‘Слово 2’, 1234], [X], [Head|Tail].
Рассмотрим [Head|Tail]. Это всё список, в котором мы выделяем первый элемент, голову списка, и остальную часть, хвост списка. Чтобы отделить первые элементы от остальной части списка, используется прямая черта «|».
Можно было написать такой список [X1,X2,X3|Tail]. Тогда мы выделим первые три элемента списка и положим их в X1, X2, X3, а остальная часть списка будет в Tail.
В списках хранятся данные, и нам нужно с ними работать. Например, находить минимум, максимум, медиану, среднее, дисперсию. Может нужно найти длину списка, длину самого длинного атома, получить средний балл по N предмету среди студентов группы G. Может нужно проверить, есть ли элемент Elem в списке List. И так далее. Короче, нужно как-то работать со списками. Только предикаты могут обрабатывать списки (да и в целом в Прологе все обрабатывается предикатами).
Напишем предикат для перебора элементов списка, чтобы понять принцип работы списка.
% element(Список, Элемент) element([Head|Tail], Element) :- Element = Head; element(Tail, Element). ?- element([1,2,3,4,5,6, 'abc', 'prolog'], Elem). Elem = 1 ; Elem = 2 ; Elem = 3 ; Elem = 4 ; Elem = 5 ; Elem = 6 ; Elem = abc ; Elem = prolog ; false.
element([Head|Tail],Element) будет истинным, если Element равен Head (первому элементу списка) ИЛИ если предикат element(Tail, Element) истинный. В какой-то момент эта рекурсия окончится. (Вопрос читателю: когда кончится рекурсия? Какое условие будет терминирующим?) Таким образом, предикат будет истинным, если Element будет равен каждому элементу списка [Head|Tail]. Пролог найдет все решения, и мы переберем все элементы списка.
Часто бывает нужным знать длину списка. Напишем предикат для нахождения длины списка. Протестим.
% list_length(Список, Длина списка) list_length([], 0). list_length([H|T], L) :- list_length(T, L1), L is L1+1. ?- list_length([123446,232,2332,23], L). L = 4. ?- list_length([123446,232,2332,23,sdfds,sdfsf,sdfa,asd], L). L = 8. ?- list_length([], L). L = 0. ?- list_length([1], L). L = 1. ?- list_length([1,9,8,7,6,5,4,3,2], L). L = 9.
Мой Пролог предупреждает, что была не использована переменная H. Код будет работать, но лучше использовать анонимную переменную _, вместо singleton переменной.
В SWI Prolog имеется встроенный предикат length. Я реализовал аналогичный предикат list_length. Если встречается пустой список, то его длина равна нулю. Иначе отсекается голова списка, рекурсивно определяется длина нового получившегося списка и к результату прибавляется единица.
Чтобы лучше понять алгоритм, пропишите его на бумаге. Последовательно, так, как делает Пролог.
Последняя задача про списки в этой статье, это определить, принадлежит ли элемент списку. Например, 1, 2, 3 и 4 являются элементами списка [1,2,3,4]. Этот предикат мы назовем list_member.
mymember(Elem, [Elem|_]). mymember(Elem, [_|Tail]) :- mymember(Elem, Tail).
Очевидно, что если список начинается с искомого элемента, то элемент принадлежит списку. В противном случае необходимо отсечь голову списка и рекурсивно проверить наличие элемента в новом получившемся списке.
Преимущества и недостатки Prolog
Пролог удобен в решении задач, в которых мы знаем начальное состояние (объекты и отношения между ними) и в которых нам трудно задать четкий алгоритм поиска решений. То есть чтобы Пролог сам нашел ответ.
Список задач, в которых Пролог удобен:
- Искусственный интеллект
- Компьютерная лингвистика. Написание стихов, анализ речи
- Поиск пути в графе. Работа с графами
- Логические задачи
- Нечисловое программирование
Знаменитую логическую задачу Эйнштейна можно гораздо легче решить на Прологе, чем на любом другом императивном языке. Одна из вариаций такой задачи:
На улице стоят пять домов. Каждый из пяти домов окрашен в свой цвет, а их жители — разных национальностей, владеют разными животными, пьют разные напитки и имеют разные профессии.
- Англичанин живёт в красном доме.
- У испанца есть собака.
- В зелёном доме пьют кофе.
- Украинец пьёт чай.
- Зелёный дом стоит сразу справа от белого дома.
- Скульптор разводит улиток.
- В жёлтом доме живет математик.
- В центральном доме пьют молоко.
- Норвежец живёт в первом доме.
- Сосед поэта держит лису.
- В доме по соседству с тем, в котором держат лошадь, живет математик.
- Музыкант пьёт апельсиновый сок.
- Японец программист.
- Норвежец живёт рядом с синим домом.
Кто пьёт воду? Кто держит зебру?
Замечание: в утверждении 6 справа означает справа относительно вас.
Научиться решать логические задачи на Пролог, можно по этой статье.
Ещё одна интересная статья. В ней автор пишет программу сочинитель стихов на Prolog.
Интересная задача, которую вы можете решить на Прологе: раскрасить граф наименьшим количеством цветов так, чтобы смежные вершины были разного цвета.

Пролог такой замечательный язык! Но почему его крайне редко используют?
Я вижу две причины:
- Производительность
- Альтернативы (например, нейросетей на Python)
Пролог решает задачи методом полного перебора. Следовательно, его сложность растет как O(n!). Конечно, можно использовать отсечения, например, с помощью «!». Но все равно сложность останется факториальной. Простые задачи не так интересны, а сложные лучше реализовать жадным алгоритмом на императивном языке.
Области, для которых предназначен Пролог, могут также успешно решаться с помощью Python, C/C++, C#, Java, нейросетей. Например, сочинение стихов, анализ речи, поиск пути в графе и так далее.
Я не могу сказать, что логическое программирование не нужно. Оно действительно развивает логическое мышление. Элементы логического программирования можно встретить на практике. И в принципе, логическое программирование — интересная парадагима, которую полезно знать, например, специалистам ИИ.
Что дальше?
Я понимаю, что статью я написал суховато и слишком «логично» (вероятно, влияние Пролога). Я надеюсь, статья вам помогла в изучении основ Логического Программирования на примере Пролога.
(Мои мысли: я часто использую повторения в статье. Это не сочинение, это туториал. Лучше не плодить ненужные синонимы и чаще использовать термины. По крайней мере, в туториалах. Так лучше запоминается. Повторение — мать учения. А как вы считаете?).
Это моя дебютная статья, и я буду очень рад конструктивной критике. Задавайте вопросы, пишите комментарии, я постараюсь отвечать на них. В конце статьи я приведу все ссылки, которые я упоминал и которые мне показались полезными.
Статью написал Горохов Михаил, успехов в обучении и в работе!
Ссылки
- Ссылка на скачивание SWI Prolog
- Синтаксис Пролога и его терминология
- Предикаты в Пролог
- Списки в Пролог
- Логические задачи
- Сочинение стихов с помощью Пролог
- И конечно же ссылка на Википедию
- Слышали о Пролог?
- Примеры использования Пролог
- prolog
- логическое программирование
- логика
- маи
- туториал
- для чайников
- введение в алгоритмы
- введение в программирование
- пми
Логическое программирование
Предикаты для ввода и вывода данных
Предикаты ввода/вывода изменяют состояние входного или выходного потока независимо от их успешности, при возврате исходное состояние потока также не восстанавливается. Для всех предикатов ввода-вывода можно указать в качестве дополнительного первого аргумента используемый поток ввода или вывода; по умолчанию ввод и вывод осуществляется в текущий поток ввода и вывода (на экран).
Предикаты set_input/1 и set_output/1 позволяют перенаправить ввод или вывод на ранее открытые файлы. Узнать текущие потоки по умолчанию можно с помощью предикатов current_input/1 и current_output/1.
Для открытия файлов используется предикат open/4, для закрытия – close/1. Первый аргумент open – атом с именем файла, второй – режим открытия (read, write или append), третий – переменная, куда будет помещен идентификатор потока, четвертый – список опций, например, тип потока (type(binary) для бинарных потоков, по умолчанию используется type(text)) или глобальное имя для потока, которое можно использовать вместо идентификатора (см. пример).
Предикат get_code осуществляют посимвольный ввод из текущего входного потока данных. Цель get_code(X) успешна, если X является ASCII-кодом очередного символом текущего входного потока. Аналогичный предикат get_byte используется для бинарных файлов.
main:-open('c:/work/input.txt',read,X,[alias(input)]), read_content(L),close(X). /* или close(input) */ read_content(L):-get_code(input,S), (S= -1 /* EOF */ -> L=[]; read_content(T),L=[S|T]).
Предикат read(X) читает очередной терм из текущего входного потока и сопоставляет его с X. Ввод терма должен заканчиваться точкой, которая не становится частью терма и удаляется из входного потока. При интерактивном вводе также следует нажать клавишу Enter. Для ввода чисел и атомов в GNU Prolog удобно использовать предикаты read_integer/1, read_number/1, read_atom/1.
Предикат put_code(X) записывает в текущий выходной поток данных символ, ASCII-код которого задан в X. Аналогичный предикат put_byte используется для бинарных файлов.
Предикат nl записывает в текущий выходной поток символы перехода на новую строку. Для форматирования можно использовать предикат tab(N), который записывает в текущий выходной поток N пробелов (нестандартный, но есть во многих реализациях Пролога).
Предикат write(X) записывает терм X в текущий выходной поток.Предикат writeq(X) записывает терм X, добавляя апострофы у некоторых атомов так, чтобы терм можно было потом считать. Предикат write_canonical(X) работает аналогично, но все объявления операторов игнорируются, что позволяет увидеть внутреннее представление структур.
?-write(1+'2*3'). 1+2*3 ?-writeq(1+'2*3'). 1+'2*3' ?-write_canonical(1+2*3). +(1,*(2,3))
Упражнения
1. Определите предикат для печати всех потомков X, используя информацию из БД о родственных связях из главы 1. Каждый потомок выводится на отдельной строке с отступом, соответствующим его дальности родства с X.
?-потомки(адам). адам каин сим . авель
2. Определите предикаты для ввода строки текста до точки и для разбиения предложения на отдельные слова по пробелам.
?-ввод_строки(L),разбиение(L,R). Это пример предложения. % вводит пользователь R=[Это,пример,предложения]
3. Определите предикат для печати арифметического выражения со скобками, указывающими порядок вычисления выражения.
?-печать(a+b*c/2-3). ((a+((b*c)/2))-3)
4. Определите предикат для печати сложного списка. Если список не содержит других списков, то выводить его целиком на одной строке, иначе каждый элемент списка выводить на отдельной строке.
?-печать_списка([a,[[d,e],f]]). [ a, [ [d,e], f ] ]