Программирование на языке Пролог на Андроиде

В данной статье я собираюсь рассказать о приложении (написанное мной) для Андроида, интерпретирующее Пролог программы написанные синтаксисом разработанным в 70-е года (тоесть в первоначальном своём виде: факты и правила, операторы и вопросы; о нём вы можете почитать в книге “Иван Братко, Программирование на языке Пролог для искусственного интеллекта”, ссылка находится в конце статьи). Называется оно Prolog Classic (ссылка для скачивания находится в конце статьи).

В настоящий момент, начиная с версии 4.0, помимо атомарных тером: атом, переменная и число, появились ещё 2: изображение и аудиозапись (читай ниже). Ещё есть возможность паралельного программирования (читай ниже).

Данное приложение написано на Java n-ide для Андроид.

1. Особенности языка PrologClassic
Итак, Пролог — язык логического программирования, основанный на языке предикатов математической логики дизъюнктов Хорна, представляющей собой подмножество логики предикатов первого порядка.

Язык сосредоточен вокруг небольшого набора основных механизмов, включая сопоставление с образцом, древовидного представления структур данных и автоматического перебора с возвратами. Хорошо подходит для решения задач, где рассматриваются объекты (в частности структурированные объекты) и отношения между ними. Пролог, благодаря своим особенностям, используется в области искусственного интеллекта, компьютерной лингвистики и нечислового программирования в целом. В некоторых случаях реализация символьных вычислений на других стандартных языках вызывает необходимость создавать большое количество кода, сложного в понимании, в то время как реализация тех же алгоритмов на языке Пролог даёт простую программу, легко помещающуюся на одной странице.

Prolog является декларативным языком программирования: логика программы выражается в терминах отношений, представленных в виде фактов и правил. Для того чтобы инициировать вычисления, выполняется специальный запрос к базе знаний, на которые система логического программирования генерирует ответы «истина» и «ложь». Для обобщённых запросов с переменными в качестве аргументов созданная система Пролог выводит конкретные данные в подтверждение истинности обобщённых сведений и правил вывода.

Задача пролог-программы заключается в том, чтобы доказать, является ли заданное целевое утверждение следствием из имеющихся фактов и правил.

В данной реализации Пролога написанной для Андроид, представлены почти все возможности изначального Пролога описанного в книге Братко [1]: безтиповое программирование; программное определение новых операторов; возможность задания нескольких вопросов в одной программе; извлечение знаний (фактов, правил, вопросов, операторов) из файлов; программная запись новых структур (фактов, правил, вопросов, операторов) в базу знаний, а также удаление из неё.

Подробнее о Прологе вы можете прочитать, также, в статье-учебнике [2] .

1.1. Задание нескольких вопросов в программе
Напишем такую программу:

мужчина(олег).
мужчина(димитрий).
женщина(алла).
женщина(маша).
?- write(мужчины:), nl,
    мужчина (М);
    write(женщины), nl,
    женщина (Ж).

Здесь, в вопросе, мы использовали точку с запятой. Она соответствует логическому ИЛИ. Это значит, что то что до точки с запятой является одним вопросом, после неё другим. Посмотрим как это работает.
Запустив программу мы увидим:
мужчины:
М = олег
Перебирая другие ответы мы получим:
мужчины:
М = олег;
М = димитрий;
женщины:
Ж = алла;
Ж = маша;
no.
Таким образом, с помощью многих вопросов мы можем получить от компьютера исчерпывающую информацию.

1.2. Параллельное программирование:
1.2.а. Паралельные правила
В реализации данного пролога существует предикат ‘::-’, который является тем же правилом, но с той разницей, что его предикаты в теле правила выполняются параллельно. Для удобства, назовём его правилом П.

Например:
ПравилоП(X)::- предикат1(X), предикат2(X), предикат3(X).

Здесь, все три предиката будут выполняться одновременно. Что касается переменных, если, к примеру, они есть в голове правила и далее, используются, скажем, во всех предикатах, то на момент запуска предикатов, будут использоваться переменные из заголовка тела, в каждом предикате, такими какими являются в голове правила (включая свободные переменные). И что важно, конкретизация переменных, из головы правила, в одном предикате, не повлияет на те же переменные из другого предиката (считайте их разными копиями). Но, после удачного выполнения эти копии между собой унифицируются. И только после этого, такое правило может быть удачным (декларативно - «истинным»).

В таком правиле тоже есть механизм возврата с поиском новых решений. С декларативной стороны он выдаст, при необходимости, все возможные решения для каждого предиката, и нам не важно как он их будет искать. Но с технической стороны, это будет происходить следующим образом. Скажем, такое правило, после первого запуска, выдаст первые решения для своих предикатов. Если нужно искать следующий результат, для правила, то сначала делается откат последнего предиката (крайний справа или снизу). Если последний предикат всё равно будет ложным, тогда откатывается предпоследний предикат и т.д. Похоже на обычное правило, не так ли? Но дальше вы узнаете, что есть и разница. Если, скажем, при выполнении правила ПравилоП(Х) (см. выше), после первого решения для правила, неудачным стал предикат предикат2(X), тогда он отправляется в конец списка (после последнего удачного предиката):
ПравилоП(X)::- предикат1(X), предикат3(X), предикат2(X).

И если после этого, неудачными будут предикаты: предикат1(X), предикат2(X), тогда, теперь первый предикат отправится перед предикатом предикат2(X) (то-есть, после последнего удачного предиката):
ПравилоП(X)::- предикат3(X), предикат1(X), предикат2(X).

А если бы, с самого начала после первого удачного решения, неудачными стали бы оба предиката предикат1(X), предикат2(X), тогда они бы оба отправились в конец списка (после последнего удачного предиката предикат3(X)):
ПравилоП(X)::- предикат3(X), предикат1(X), предикат2(X).

Как видите, порядок отправляющихся одновременно предикатов сохраняется: слева на право.
Все эти перемещения и порядок нужны для того, чтобы получить ВСЕ решения, как если бы они выполнялись последовательно.

1.2.б. Параллельные вопросы
Поговорим ещё об одном виде параллельного программирования - это параллельные вопросы (операторы: ‘!-’ и ‘?-’).

Параллельные вопросы бывают двух видов:

  1. У первых предикаты выполняются точно также - последовательно, - как и у главного вопроса, за исключением, что эти параллельные вопросы выполняются одновременно с главным и параллельными вопросами.
    Например:
    cons(вопрос2) ?- предикат1, предикат2.

Левая часть предиката должна обязательно состоять из функтора cons и одного атомарного аргумента, который не может быть переменной (идентификатор вопроса).

  1. У вторых предикаты выполняются параллельно, как в правилах П, ну и конечно, сами вопросы выполняются одновременно с главным и параллельными вопросами.
    Например:
    par(вопрос2) ?- предикат1, предикат2.

Левая часть предиката должна обязательно состоять из функтора par и одного атомарного аргумента, который не может быть переменной (идентификатор вопроса).

Теперь, поговорим немножко о том что будет, если нехватит ОЗУ при выполнении параллельных правил П.

Допустим, у вас, выполняется параллельное правило правП1 с предикатами: предикат1, предикат2. Итак, предикат1 и предикат2 выполняются параллельно. Что же будет, если во время их выполнения не хватит оперативной памяти? А будет то, что в какой-то момент, один предикат будет выполняться дальше, а другой будет ждать, пока не выполнится первый. А если быть точнее, пока не освободится достаточно памяти. А поскольку надо, чтобы не просто выполнился один из предикатов, а после выполнения одного снова освободилась память, - я советую делать следующее. Допустим, вам известно, что с правиломП может такое случиться. Тогда, с каждым его предикатом (предикат1, предикат2 и т.д.) нужно сделать следующую трансформацию:
предикат1_(M,X):- предикат1(X), assertA (p(M,X)), false; retractA (p(M,X)).

Мы создали новый предикат предикат1_(M,X), в котором при удачном выполнении предиката предикат1(X), сохраняется результат его выполнения в факт p(M,X), где М - это уникальный идентификатор предиката (чтобы не путался факт одного предиката с фактом другого). Таким образом, после выполнения предиката предикат1(X), весь стэк освобождается до момента его выполнения, что позволяет дальше выполняться другим параллельным предикатам.

1.3. Работа с медиа
В приложении PrologClassic, начиная с версии 4.0, есть добавления в синтаксисе языка - работа с медиа (на сегодняшний день - без видео). Изображения и аудиозаписи - это атомарные термы. Медиа, на данный момент, больше всего нужно для работы с нейросетями.

Внимание: если у вас в программе добавлено медиа, то такую программу нужно сохранять в отдельную папку, потому что, вместе с программой сохраняется и медиа из неё.

2. Встроенные предикаты

!/0 - Отсечение.
atom/1 - Является истинным, если аргумент является атомом.
chars/2 - Является истинным, если 1-ый аргумент - список символов из которых состоит функтор терма 2-ого аргумента.
retractA/1 - Является истинным, если в базе знаний существет такой факт или правило (аргумент), которое есть предикат, и удаляет его. Поиск ведётся с начала базы знаний.
retractZ/1 - Является истинным, если в базе знаний существет такой факт или правило (аргумент), которое есть предикат, и удаляет его. Поиск ведётся с конца базы знаний.
assertA/1 - Всегда является истинным и добавляет аргумент как факт или правило в начало базы знаний.
assertZ/1 - Всегда является истинным и добавляет аргумент как факт или правило в конец базы знаний.
read/1 - Читает терм с текущего потока ввода (консоль или файл) и является истинным, если прочитанный терм равен терму аргумента.
write/1 - Всегда является истинным и выводит терм аргумента в текущий потока вывода (консоль или файл).
nl/0 - Всегда является истинным и выводит поток вывода (консоль или файл) на новую строку.
tab/1 - Является истинным, если аргумент - это число, и выводит в поток вывода (консоль или файл) некоторое количество пробелов указанных в аргументе.
numeral/1 - Всегда является истинным, если терм аргумента является числом.
<=/2 - Является истинным, если число 1-го аргумента меньше или равно числу 2-го аргумента.
</2 - Является истинным, если число 1-го аргумента меньше числу 2-го аргумента.
>=/2 - Является истинным, если число 1-го аргумента больше или равно числу 2-го аргумента.
>/2 - Является истинным, если число 1-го аргумента больше числа 2-го аргумента.
=/2 - Является истинным: 1) Если одну или две стороны можно вычислить, а потом унифицировать; 2) В противном случае, если можно просто унифицировать левую и правую часть равенства. Изображения звукозаписи и сравниваются по-байтово.
var/1 - Является истинным, если аргумент является свободной переменной.
see/1 - Является истинным, если аргумент это атом в котором указан путь и имя существующего файла. Предикат устанавливает данный файл как поток ввода. Если аргумент равен user потоком ввода будет консоль, при этом открытые файлы не закрываются.
seen/0 - Является истинным, если текущий поток ввода является файлом, который и закрывает.
time/1 - Является истинным, если аргумент равен прошедшему времени в миллисекундах начиная от 00:00 01.01.1970 года (этот предикат полезен для генерации случайных чисел).
not/1 - Логическое отрицание. Истиннен, если предикат аргумента является ложным в любом случае.
fchar/3 - Истинен, если 1-ый аргумент - это первый символ от строкого атомарного терма 3-го аргумента, а 2-ой аргумент - это остаток.
string/3 - Истинен, если первые 2-а аргумента - строковые атомарные термы, и при слиянии их строк равны строке 3-го атомарного строкого аргумента.
name/2 - Истинен, если 1-ый аргумент атомарный строковый терм и его строка распадается на список кодов его символов.
tell/1 - Истинен, если его аргумент - это атом, в котором указан путь и имя существующего файла. Предикат устанавливает данный файл как поток вывода. Если аргумент равен user потоком вывода будет консоль. Если аргументом является свободная переменная, тогда мы просто получаем путь (оно же его имя) файла текущего потока вывода.
told/0 - Истинный, если текущий поток вывода является файлом, который и закрывается.
delete_file/1 - Истинен, если возможно удалить файл указанный в аргументе, что и делает предикат.
create_file/1 - Истинен, если возможно создать файл.
=:/2 - Истинен, если справа у нас список, у которого первый элемент - это функтор терма стоящего слева от оператора, а остальные элементы (если они есть) - его аргументы.
ucase/2 - Истинен, если 2-ой аргумент это атом с возведёнными символами в верхний регистр символами 1-го аргумента. 1-ый аргумент должен быть обязательно определён.
lock_up_writeread/0 - Предоставляет текущему вопросу выводить на экран или в файл или читать аналогично, не позволяя другим параллельно выполняющимся вопросам делать в этот момент тоже самое.
unlock_writeread/0 - Разблокировка ввода-вывода, если в вопросе, из которого он вызван, была использована блокировка (lock_up_writeread).
mark/1 - Используется для получения заголовка вопроса являющийся индивидуальным идентификатором.
synchronize/1 - Синхронизирует код с другими потоками (параллельными вопросами или правилами), у которого аргумент должен быть атомарным, являющийся меткой для синхронизации с другим таким же кодом. Работа его такова: когда два или несколько вопросов или параллельных предикатов доходят до этого предиката с одной и той же меткой, у одного код выполнятся дальше, остальные ждут.
unsynchronize/1 - Завершает синхронизацию кода с определённой меткой.
image/1 - Истинен, если терм является изображением.
sound/1 - Истинен, если терм является аудиозаписью.
img_width/2 - Изменяет ширину картинки, а вместе с ней и высоту пропорционально ширине. 1-ый аргумент - это само изображение, а 2-ой - величина новой ширины в пикселях.
connection/2 - Задаёт авторизационные данные для работы с нейросетями. Для Яндекса - это Oauth яндекс-аккаунта и Id папки. Для Сбера - это Авторизационные данные и Client Secret (RqUID).
chat_YandexGPT/5 - Генерирует текст нейросетью YandexGPT. Аргументы: 1-ый - это атом представляющий из себя системный текст запроса; 2-ой - список атомов представляющих из себя пользовательский текст с ответами нейросети; 3-ий - тип нейросети: YandexGPT Lite, YandexGPT Pro и YandexGPT Pro 32k; 4-ый - температура; 5-ый - это атом: ответ нейронной сети.
chat_GigaChat/6 - генерирует текст нейросетью GigaChat. Аргументы: 1-ый - это атом представляющий из себя системный текст запроса; 2-ой - список атомов представляющих из себя пользовательский текст с ответами нейросети; 3-ий - список изображений использующиеся в текущем запросе к нейросети; 4-ый - модель нейросети: GigaChat, GigaChat-Pro, GigaChat-2-Max и т.д.; 5-ый - температура; 6-ой - это атом: ответ нейронной сети.
chat_YandexART/6 - Генерирует изображение нейросетью YandexART. Аргументы: 1-ый - это атом представляющий из себя текст запроса; 2-ой и 3-ий - соотношение сторон изображения; 4-ый - зерно генерации, по умолчанию ставим 0; 5-ый - вес текстового описания, по умолчанию ставим 1; 6-ой - это атомарный терм - изображение: ответ нейронной сети.
chat_GigaChat_image/4 - Генерирует изображение нейросетью Гигачатом (если быть точным - это Кандински в оболочке Гигачата). Аргументы: 1-ый - атом представляющий из себя системный текст запроса; 2-ой - атом представляющий из себя обычный текст запроса; 3-ий - модель нейросети: GigaChat, GigaChat-Pro, GigaChat-2-Max и т.д.; 4-ый - ответ нейронной сети ввиде изображения.
chat_YandexSpeechKitv1/6 - Синтез речи нейросетью Яндекса. Аргументы: 1-ый - текст для синтеза; 2-ой - атом представляющий из себя тип языка (например русский: ‘ru-Ru’); 3-ий - задать голос, например: filipp; 4-ый - амплуа голоса, например: neutral; 5-ый - скорость речи; 6-ой - ответ нейронной сети ввиде аудиозаписи.
chat_SberSaluteSpeech/3 - Синтез речи нейросетью Сбера. Аргументы: 1-ый - текст для синтеза; 2-ий - голос, например: Pon_24000; 3-ой - ответ нейронной сети ввиде аудиозаписи.

3. Использование интерпретатора Пролог на Android
Поскольку приложение является интерпретатором — оно не создаёт apk файл. С другой стороны это может быть даже удобно, так как пользователь всегда может что-то изменить в программе по своему усмотрению, или просто посмотреть как написана программа и из текста понять её работу целиком или каких-то частей.

Скриншоты приложения:


Данное приложение может работать только с консолью, такова была моя задумка. Я искал такое приложение, для написания программ с искуственным интеллектом, под Андроид и в Google play и в поисковике, но нигде не найдя пришлось написать самому.

Заключение

В конце, хочется сказать, что данное приложение работая интерпретатором и в консоли предназначено скорее для написания научно-исследовательских работ у которых код программы всегда открыт и имеется возможность улучшить или изменить программу. Пролог — это язык логического программирования, но содержит и свойства процедурного исполнения программ (бывает, важен порядок выполнения предикатов; отсечение [3]), что очень похоже на то как думает наш мозг. Поэтому он очень хорошо подходит для написания программ с искусственным интеллектом. В качестве примера, можете посмотреть программный код игры «крестики и нолики» [4], в котором для человека, который не одну программу написал на Прологе, многие места в программе будут легко понятны, да и программа маленькая. Также приложение позволяет работать с нейросетями от Яндекса и Сбера. В качестве примера, можете посмотреть программный код программы «Сочинитель сказки» [5].

Моя почта: elevferii_pechori@mail.ru.

Список литературы
1. Братко И. Программирование на языке Пролог для искусственного интеллекта.- М., 1990.
2. Введение в логическое программирование (Prolog). URL:
https://pro-prof.com/archives/2362
3. Принципы построения программ на Prolog. URL:
https://pro-prof.com/forums/topic/semantics-prolog
4. Программный код игры в «крестики и нолики» с искусственным интеллектом. URL: https://disk.yandex.ru/d/5ED-43Uveub1ew
5. Программный код программы «Сочинитель сказки». URL: https://disk.yandex.ru/d/5ED-43Uveub1ew
6. Prolog Classic | Интерпретатор Пролог программ с выводом в консоль. NashStore: https://store.nashstore.ru/store/651cf1c60a39b23f221fc121
или Яндекс Диск:
https://disk.yandex.ru/d/5ED-43Uveub1ew
7. Исходный код интерпретатора Prolog-программ на Android (PrologClassic). URL:
https://disk.yandex.ru/d/AVxfoYbg07ZcnA%5Bprolog%5D

Не делайте абзацы с 4 пробелами )

В статьях в интернете обычно так не делают, а делают просто пустую строку между абзацами.
В лучшем случае эти пробелы просто не отобразятся. В маркдауне отступы в начале строки распознаются как форматирование кода. FAQ по функциональности форума, отличия от классических форумных движков

Изменения связанные с версией 2.3:

  1. Имя предиката functor_chars/2 изменено на chars/2. Где первый аргумент теперь должен быть строго атомарным;
  2. Добавлены предикаты:
    а) fchar/3, где 1-ый аргумент - это первый символ от атомарного терма 3-го аргумента, а 2-ой аргумент - это остаток;
    б) string/3, который истинен, если первые 2-а атомарные аргумента, при слиянии имён равны имени 3-го атомарного аргумента;
    в) name/2, который истинен, если 1-ый аргумент атомарен и его имя распадается на список кодов его символов;
    г) tell/1, который истинен, если его аргумент - это атом, в котором указан путь и имя существующего файла. Предикат устанавливает данный файл как поток вывода. Если аргумент равен user потоком вывода будет консоль. Если аргументом является свободная переменная, тогда мы просто получаем путь (оно же его имя) файла текущего потока вывода;
    д) told/0, который является истинным, если текущий поток вывода является файлом, который и закрывается;
    е) delete_file/1, который истинен, если возможно удалить файл указанный в аргументе, что и делает предикат;
    ё) create_file/1, который истинен, если возможно создать файл;
    ж) инфиксный оператор =:, который истинен, если справа у нас список, у которого первый элемент - это имя терма стоящего слева от оператора, а остальные элементы (если они есть) - его аргументы.

Изменения в приложении с версией 2.4:

  1. При запуске приложения вместе с окном появляется сервисное уведомление в шторке уведомлений. Оно нужно для того, чтобы во первых, после закрытия окна его можно было восстановить с текущими данными (на андроид 8 это не работает; на 9 и 10 не проверял), тоесть даже если вы закроете окно, например, при работе программы, все данные которые программа будет выводить в консоль, после восстановления окна, появятся на экран. Во вторых, теперь из шторки уведомлений можно прервать работу программы, например при случайном зацикливании. Приложение, теперь, полностью закрывается только из шторки уведомлений;
  2. Запущенная программа, теперь будет работать и при выключенном экране (Внимание: андроид на xiaomi так устроен, что приложение сильно потребляющее ресурсы, тоесть при работе сложной программы, может быть убито, если окно свёрнуто: не закрыто, не видимо, а именно свёрнуто; имейте ввиду!);
  3. Добавлен поиск в тексте;
  4. Добавлено окно со списком всех операторов, тоесть встроенные и те, которые написаны в программе;
  5. Добавлен предикат ucase/2, у которого второй аргумент это строка с возведёнными символами в верхний регистр строки первого аргумента. Первый аргумент должен быть обязательно определён;
  6. Приложение, призапуске неправильной программы, более подробно показывает ошибку;
  7. Минимаeльные требования: андроид 8.0.

Изменения в приложении с версией 2.5:

  1. Ускорена работа редактора и интерпретатора;
  2. Переменные окрашиваются зелёным цветом;
  3. Добавлен встроенный предикат consult(F), где F - это путь и имя файла. При чтении с консоли (consult(user)), end_of_consult является меткой завершения чтения, после которой должна сразу стоять точка;
  4. Добавлен встроенный предикат lcase(A1,A2), где A2 - это строка A1 превидённая к нижнему регистру;
  5. Добавлен встроенный предикат e(E), Где E - это число e;
  6. Добавлен оператор exp (A1 exp A2), число A1 возводится в степень A2.
  7. Подобие: ?- X. - теперь работает всегда. Например:
    а) ?- X = (Y=5+3), X. Результат: X = ( 8 = 5 + 3 ), Y = 8.
    Или
    б) ?- X = not(7=5+3), X. Результат: X = not ( 7 = 5 + 3 ), потому что, предикат в переменной истинен.

Почему мы должны изучать этот prolog, а не haskell? Пролог отличается тем, что в нем есть жручая “машина логического вывода”, которую надо императивно ограничивать отсечениями перебора, иначе всё будет безбожно тормозить. Вот просто почему сразу не писать хорошо? Да ещё распараллеливаемо для современных многоядерных процессоров (которые, да, в смартфонах).

Не понимаю, что можно распараллелить в логической программе. Это не нейронные сети же, чтобы их параллелить?!

очень обидно смотреть на то, как в процессоре задействовано одно ядро из 32-х, а процесс работы при этом длится днями.

Согласен. Я в таких случаях запускаю ещё клон приложения. (Приложение, для тяжёлой программы, обязательно должно работать или в фоне, или в переднем окне, или в виде плавающего окна, потому что тяжёлое свёрнутое приложение miui от ксиаоми обязательно зарубит).
Так что, когда работают несколько приложений, вероятность получить стих раньше возрастает. :thinking:

Изменения в приложении с версией 3.0:

  1. Параллельное программирование:
    С новой версией приложения появился предикат ‘::-’, который является тем же правилом, но с той разницей, что его предикаты в теле правила выполняются параллельно. Для удобства, назовём его правилом П.
    Например:
    ПравилоП(X)::- предикат1(X), предикат2(X), предикат3(X).
    Здесь, все три предиката будут выполняться одновременно. Что касается переменных, если, к примеру, они есть в голове правила и далее, используются, скажем, во всех предикатах, то на момент запуска предикатов, будут использоваться переменные из заголовка тела, в каждом предикате, такими какими являются в голове правила (включая свободные переменные). И что важно, конкретизация переменных, из головы правила, в одном предикате, не повлияет на те же переменные из другого предиката (считайте их разными копиями). Но, после удачного выполнения эти копии между собой унифицируются. И только после этого, такое правило может быть удачным (декларативно - «истинным»).
    В таком правиле тоже есть механизм возврата с поиском новых решений. С декларативной стороны он выдаст, при необходимости, все возможные решения для каждого предиката, и нам не важно как он их будет искать. Но с технической стороны, это будет происходить следующим образом. Скажем, такое правило, после первого запуска, выдаст первые решения для своих предикатов. Если нужно искать следующий результат, для правила, то сначала делается откат последнего предиката (крайний справа или снизу). Если последний предикат всё равно будет ложным, тогда откатывается предпоследний предикат и т.д. Похоже на обычное правило, не так ли? Но дальше вы узнаете, что есть и разница. Если, скажем, при выполнении правила ПравилоП(Х) (см. выше), после первого решения для правила, неудачным стал предикат предикат2(X), тогда он отправляется в конец списка (после последнего удачного предиката):
    ПравилоП(X)::- предикат1(X), предикат3(X), предикат2(X).
    И если после этого, неудачными будут предикаты: предикат1(X), предикат2(X), тогда, теперь первый предикат отправится перед предикатом предикат2(X) (то-есть, после последнего удачного предиката):
    ПравилоП(X)::- предикат3(X), предикат1(X), предикат2(X).
    А если бы, с самого начала после первого удачного решения, неудачными стали бы оба предиката предикат1(X), предикат2(X), тогда они бы оба отправились в конец списка (после последнего удачного предиката предикат3(X)):
    ПравилоП(X)::- предикат3(X), предикат1(X), предикат2(X).
    Как видите, порядок отправляющихся одновременно предикатов сохраняется: слева на право.
    Все эти перемещения и порядок нужны для того, чтобы получить ВСЕ решения, как если бы они выполнялись последовательно.
    Теперь поговорим ещё об одном виде параллельного программирования - это параллельные вопросы (операторы: ‘!-’ и ‘?-’).
    Параллельные вопросы бывают двух видов:
  1. У первых предикаты выполняются точно также - последовательно, - как и у главного вопроса, за исключением, что эти параллельные вопросы выполняются одновременно с главным и параллельными вопросами. Например:
    cons(вопрос2) ?- предикат1, предикат2.
    Левая часть предиката должна обязательно состоять из функтора cons и одного атомарного аргумента, который не может быть переменной (идентификатор вопроса).
  2. У вторых предикаты выполняются параллельно, как в правилах П, ну и конечно, сами вопросы выполняются одновременно с главным и параллельными вопросами. Например:
    par(вопрос2) ?- предикат1, предикат2.
    Левая часть предиката должна обязательно состоять из функтора par и одного атомарного аргумента, который не может быть переменной (идентификатор вопроса).
    Теперь, поговорим немножко о том что будет, если нехватит ОЗУ при выполнении параллельных правил П.
    Допустим, у вас, выполняется параллельное правило правП1 с предикатами: предикат1, предикат2. Итак, предикат1 и предикат2 выполняются параллельно. Что же будет, если во время их выполнения не хватит оперативной памяти? А будет то, что в какой-то момент, один предикат будет выполняться дальше, а другой будет ждать, пока не выполнится первый. А если быть точнее, пока не освободится достаточно памяти. А поскольку надо, чтобы не просто выполнился один из предикатов, а после выполнения одного снова освободилась память, - я советую делать следующее. Допустим, вам известно, что с правиломП может такое случиться. Тогда, с каждым его предикатом (предикат1, предикат2 и т.д.) нужно сделать следующую трансформацию:
    предикат1_(M,X):- предикат1(X), assertA (p(M,X)), false; retractA (p(M,X)).
    Мы создали новый предикат предикат1_(M,X), в котором при удачном выполнении предиката предикат1(X), сохраняется результат его выполнения в факт p(M,X), где М - это уникальный идентификатор предиката (чтобы не путался факт одного предиката с фактом другого). Таким образом, после выполнения предиката предикат1(X), весь стэк освобождается до момента его выполнения, что позволяет дальше выполняться другим параллельным предикатам.
  1. Добавлены предикаты:
    а) lock_up_writeread/0. Предоставляет текущему вопросу выводить на экран или в файл или читать аналогично, не позволяя другим параллельно выполняющимся вопросам делать в этот момент тоже самое;
    б) unlock_writeread/0. Разблокировка ввода-вывода, если в вопросе, из которого он вызван, была использована блокировка (lock_up_writeread);
    в) mark/1. Используется для получения заголовка вопроса являющийся индивидуальным идентификатором. Например:
  • par(1)!- mark(M).
    M = par(1).
  • cons(2)?- mark(M).
    M = cons(2).
  • Но: ?- mark(M).
    M = ?.
    г) synchronize/1. Синхронизирует код с другими потоками (параллельными вопросами или правилами), у которого аргумент должен быть атомарным, являющийся меткой для синхронизации с другим таким же кодом. Работа его такова: когда два или несколько вопросов или параллельных предикатов доходят до этого предиката с одной и той же меткой, у одного код выполнятся дальше, остальные ждут;
    д) unsynchronize/1. Завершает синхронизацию кода с определённой меткой.

Это как-нибудь согласуется с какими-нибудь стандартами на пролог, или теперь получился несовместимый язык?

Это просто добавление. Всё что должно быть в прологе - здесь есть. Это просто добавление.
Хотите пользуйтесь - хотите нет.

Ну вообще, PrologClassic похож на SWI-Prolog. Его приемущество в том, что он работает на Андроиде.

приемущество

Это не аргумент. На андроиде надо программировать на том, на чём программируют для андроида максимальное количество людей, то есть на Java.

Аргумент изучать пролог должен крыться в каких-то уникальных возможностях, даруемых самим прологом. Но их не существует.

Но я и не думал выставлять его для создания коммерческих приложений. Не зря же он работает в консоли. Для обучения прологу разве не сгодится? SWI-Prolog , вроде, на андроиде не работает. А этот - да. Разве не удобно иметь корманное приложение, которое всегда с тобой?
Может я в статье, написал что-то лишнее, прошу прощения.

Изменения в версии 3.2:
С новой версией добавлен умный помощник (его зовут Ванечка). Этот помощник основан на базе генеративной языковой модели.


Для того, чтобы этот помощник появился нужно при первом запуске приложения или в настройках выбрать желаемую GPT на базе которой помощник будет работать (YandexGPT или GigaChat) и ввести авторизационные данные (после подключения в облаке, например Yandex Cloud) услуги работы с API генеративной модели (как это сделать - читайте документацию для соответствующей «сетки»).

Возможности помощника:

  1. может предоставить общую справочную информацию по PrologClassic и вообще по прологу, например:

  2. может предоставить справочную информацию по встроенным операторам и встроенным предикатам, например:

  3. может написать небольшой программный код (в запросе должны обязательно присутствовать ключевые слова: «программный код»), например:

Некоторые рекомендации:

  1. Касательно GigaChat Lite (в приложении обозначена как GigaChat);
  2. В чате, после запроса на написание программного кода, если нужно получить справочную информацию - нужно очистить чат, т.к. после написания кода сетка ещё будет думать об этом коде и не поймёт что вы от неё хотите.

Отличный маркетинговый материал. Жаль, что я не покупатель.

Изменения в приложении с версией 3.4:

  1. возможность использовать в функторе терма одинарную ковычку добавив её двумя одинарными ковычками, например:
'Вася ''лентяй'''(двоечник) или 'минералка ''боржоми'''
  1. добавлен предикат speak/3 позволяющий синтезировать речь средствами ОС Андроид, например:
speak ('Привет. Меня зовут Вася',1,1)

Второй параметр указывает тембр голоса, третий - скорость произношения. Предикат произносит предложения на русском языке, голос же, например мужской или женский, выбирается в настройках Андроид. В качестве примера его работы, можно скачать и запустить файл с программой “Крестики и нолики 1.5.txt” (файл находится здесь ). Это написанная мной программа игры в крестики и нолики с компьютером, каждый ход озвучивается компьютером.

Изменения в приложении с обновлением 3.41:
• добавлен предикат sleep/1 , заставляющий вопрос остановиться на столько, сколько указано мелисикунд в аргументе, например:

sleep (1000)

Изменения в приложении с версией 4.0:

  1. Новые типы данных.
    Теперь атомарный терм может быть не только атомом, числом и переменной, но ещё и изображением, и аудиозаписью.

  2. Появились новые предикаты:
    image/1 - является ли терм изображением;
    sound/1 - является ли терм аудиозаписью;
    img_width/2 - изменить ширину картинки, а вместе с ней и высоту пропорционально ширине. Первый аргумент - это само изображение, а второй - величина новой ширины в пикселях;
    connection/2 - задаёт авторизационные данные для работы с нейросетями. Для Яндекса - это Oauth яндекс-аккаунта и Id папки. Для Сбера - это Авторизационные данные и Client Secret (RqUID);
    chat_YandexGPT/5 - генерирует текст нейросетью YandexGPT. Аргументы: 1-ый - это атом представляющий из себя системный текст запроса; 2-ой - список атомов представляющих из себя пользовательский текст с ответами нейросети; 3-ий - тип нейросети: YandexGPT Lite, YandexGPT Pro и YandexGPT Pro 32k; 4-ый - температура; 5-ый - это атом: ответ нейронной сети;
    chat_GigaChat/5 - генерирует текст нейросетью GigaChat. Аргументы: 1-ый - это атом представляющий из себя системный текст запроса; 2-ой - список атомов представляющих из себя пользовательский текст с ответами нейросети; 3-ий - тип нейросети: GigaChat Lite, GigaChat Pro и GigaChat Max; 4-ый - температура; 5-ый - это атом: ответ нейронной сети;
    chat_YandexART/6 - генерирует изображение нейросетью YandexART. Аргументы: 1-ый - это атом представляющий из себя текст запроса; 2-ой и 3-ий - соотношение сторон изображения; 4-ый - зерно генерации, по умолчанию ставим 0; 5-ый - вес текстового описания, по умолчанию ставим 1; 6-ой - это атомарный терм - изображение: ответ нейронной сети;
    chat_GigaChat_image/4 - генерирует изображение нейросетью Гигачатом (если быть точным - это Кандински в оболочке Гигачата). Аргументы: 1-ый - атом представляющий из себя системный текст запроса; 2-ой - атом представляющий из себя обычный текст запроса; 3-ий - тип нейросети: GigaChat Lite, GigaChat Pro и GigaChat Max; 4-ый - ответ нейронной сети ввиде изображения;
    chat_YandexSpeechKitv1/6 - синтез речи нейросетью Яндекса. Аргументы: 1-ый - текст для синтеза; 2-ой - атом представляющий из себя тип языка (например русский: ‘ru-Ru’); 3-ий - задать голос, например: filipp; 4-ый - амплуа голоса, например: neutral; 5-ый - скорость речи; 6-ой - ответ нейронной сети ввиде аудиозаписи.

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


Технология отличная. Сказка плохая. У нас в стране демографический кризис как раз из-за сказок с подобным содержимым. Политики, воспитанные на подобных сказках, не в состоянии принять правильные законы (как, например, недавно приняли в Китае).