В данной статье я собираюсь рассказать о приложении (написанное мной) для Андроида, интерпретирующее Пролог программы написанные синтаксисом разработанным в 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.б. Параллельные вопросы
Поговорим ещё об одном виде параллельного программирования - это параллельные вопросы (операторы: ‘!-’ и ‘?-’).
Параллельные вопросы бывают двух видов:
- У первых предикаты выполняются точно также - последовательно, - как и у главного вопроса, за исключением, что эти параллельные вопросы выполняются одновременно с главным и параллельными вопросами.
Например:
cons(вопрос2) ?- предикат1, предикат2.
Левая часть предиката должна обязательно состоять из функтора cons и одного атомарного аргумента, который не может быть переменной (идентификатор вопроса).
- У вторых предикаты выполняются параллельно, как в правилах П, ну и конечно, сами вопросы выполняются одновременно с главным и параллельными вопросами.
Например:
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