Показаны сообщения с ярлыком prolog. Показать все сообщения
Показаны сообщения с ярлыком prolog. Показать все сообщения

8 мая 2015 г.

Первая в мире реализация игры крестики-нолики на Prolog

Крестики-нолики на платформе Гедымин. Игровой движок реализован на Prolog. Исходный код тут.


Клонируйте проект gedemin-prolog. Устанавливайте пространство имен GS.Пролог.Учебный пример.Игра крестики-нолики.yml. Получайте удовольствие!

PS: получится обыграть машину на максимальном поле и уровне сложности -- обязательно пишите сюда. 

4 апр. 2014 г.

Пролог

Аналогов в Беларуси просто нет. Ниже приведен фрагмент кода программы расчета заработной платы на языке Prolog . Работает в режиме промышленной эксплуатации на двух крупных предприятиях (более 1000 работников на каждом).

% среднедневной заработок по сотруднику (по среднечасовому)
% - для отпусков
calc_avg_wage(Scope, PK, AvgWage, Rule) :-
Rule = by_avg_houre,
% правило действительно
is_valid_rule(Scope, PK, _, Rule),
% подготовка временных данных для расчета
prep_avg_wage(Scope, PK, Periods),
% взять заработок
findall( Wage,
% за каждый период проверки
( member(Y1-M1, Periods),
% взять данные по заработку
get_month_wage(Scope, PK, Y1, M1, _, Wage) ),
% в список заработков
Wages ),
% итоговый заработок
sum_list(Wages, Amount),
% взять часы
findall( THoures,
% за период проверки
( member(Y2-M2, Periods),
% взять данные по часам за месяц
get_month_norm_tab(Scope, PK, Y2-M2, _, _, _, THoures)
),
% в список часов
Durations),
% всего часов по табелю
sum_list(Durations, TotalTab),
% среднечасовой заработок
catch( AvgHoureWage is Amount / TotalTab, _, fail ),
% разложить первичный ключ
PK = [pEmplKey-EmplKey, pFirstMoveKey-FirstMoveKey],
% всего часов по графику за расчетный год
get_data(Scope, in, usr_wg_TblYearNorm, [
fEmplKey-EmplKey, fFirstMoveKey-FirstMoveKey,
fWHoures-TotalNorm]),
% среднемесячное количество расчетных рабочих часов
AvgMonthNorm is TotalNorm / 12,
% среднемесячное количество календарных дней
get_param(Scope, in, pAvgDays-AvgDays),
% среднедневной заработок
catch( AvgWage0 is AvgHoureWage * AvgMonthNorm / AvgDays, _, fail ),
AvgWage is round(AvgWage0),
!.

13 нояб. 2013 г.

Обработка результата Пролог вычислений

Результат выполнения Пролога нужен разработчику для дальнейшей обработки.

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

Почему таблица?

  • Можно использовать в запросах.
  • Можно подключать все существующие контролы.
Временные таблицы уровня транзакции -- идеальные кандидаты на эту роль. Если мы придем к единой структуре для хранения результата на все случаи жизни, то можно ввести набор таких таблиц в эталонную бд.

5 нояб. 2013 г.

Задача на Прологе для платформы Гедымин

Поскольку это первый пример в книге Ивана Братко (классиков нужно чтить!) задача будет такой:
  1. В стандартный справочник людей (GD_PEOPLE) добавить два поля -- отец и мать. Необязательные для заполнения ссылки на этот же справочник.
  2. Реализовать на Прологе правила: предок, потомок, сын, дочь, внук, внучка, дедушка, бабушка, родственник, близкий родственник, член семьи, брат, сестра, сводный брат, сводная сестра.
  3. Реализовать диалоговое окно в котором можно:
    1. Выбрать человека, степень родства и получить список удовлетворяющих лиц.
    2. Выбрать двух людей и получить список степеней родства между ними.
    3. Выбрать двух людей, степень родства и получить ответ Да или Нет.
PS: Завтра постараемся добавить нужные Прологу файлы, чтобы заодно и инстолятор протестировать.

29 авг. 2013 г.

Подключение Embedded SWI-Prolog в Гедымин

Есть два варианта подключения Пролога. Первый -- мы обращаемся к embedded SWI-Prolog из кода на VBScript через глобальный объект (назовем его PL). Второй -- алгоритм пишется непосредственно на Прологе, а доступ к функционалу платформы происходит через Foreign Predicates.

Обращение к Прологу через глобальный объект мы рассмотрим на сравнительном примере доступа к реляционным данным из макроса на Гедымине. Пусть данные находятся в двух таблицах: GD_CONTACT(ID, CITYKEY, NAME) и GD_PLACE(ID, NAME):

ID  CITYKEY  NAME
======================
1   1        Company A
2   1        Company B
3   2        Company C

ID  NAME
======================
1   Minsk
2   Berioza 
Для извлечения cписка компаний по заданному наименованию города мы пишем SQL запрос, который выполняем через объект класса TIBSQL:
...
Dim q
Set q = Creator.GetObject(nil, "TIBSQL", "")
Set q.Transaction = gdcBaseManager.ReadTransaction
q.SQL.Text = _
  "SELECT c.name " & _
  "FROM gd_contact c JOIN gd_place p " & _
  "  ON p.id = c.citykey " & _
  "WHERE p.name = 'Minsk' "
q.ExecQuery
While Not q.EOF 
  ...
  q.Next
WEnd 
...
Теперь изобразим тоже самое на Prolog.

Исходные данные в виде предикатов Prolog выглядят так:

gd_contact(1, 1, 'Company A').
gd_contact(2, 1, 'Company B').
gd_contact(3, 2, 'Company C').

gd_place(1, 'Minsk').
gd_place(2, 'Berioza').
Правило связи компании с городом через ключи:
bycity(City, Name) :- 
  gd_place(CityID, City),
  gd_contact(_, CityID, Name).
Тогда запрос на извлечение всех минских компаний будет выглядеть следующим образом:
? bycity('Minsk', Name).
Что мы видим из данного примера?

  1. Необходим класс для доступа к Прологу из макросов. Далее по тексту будем называть его PL. Экземпляр данного класса создается стандартным образом через глобальный объект Designer/Creator. Каждый экземпляр может иметь свое имя.
  2. PL должен иметь метод для формирования предикатов на основании заданной SQL выборки. Пример кода:
    ...
    Call PL.MakePredicates(_
      "SELECT id, citykey, name FROM gd_contact", _
      gdcBaseManager.ReadTransaction, _ 
      "gd_contact", "name")
    ...
    
    Последний параметр -- имя выборки, которое будет использоваться для формирования имени .pl файла в режиме отладки.

    Пусть это и не касается непосредственно сопряжения Пролога с Гедымином, но здесь стоит вспомнить об идее изоляции разработчика от работы с SQL запросами и инкапсуляции последних внутрь т.н. SQL объекта. SQL объект -- это заранее написанный, проверенный и оптимизированный SQL запрос, загружаемый на базу стандартным образом через настройки или пространства имен. Для выполнения, программист обращается к нужному SQL объекту по его РУИДу.

  3. PL должен иметь метод для формирования предикатов на основании бизнес-класса. На вход передается класс-подтип, подмножество, дополнительные условия выборки. Сразу следует договориться о стандартном механизме (способе) загрузки данных мастер-дитэйл (например, документ накладная с позициями).
    ...
    Call PL.MakePredicates2(_
      "TgdcCompany", "", "ByID", Array(777777), "", _
      gdcBaseManager.ReadTransaction, _ 
      "gd_contact", "name")
    ...
    
  4. PL должен иметь метод для формирования предикатов на основании существующего набора данных (дата-сета).
  5. PL должен иметь метод для ручного формирования произвольных предикатов.
  6. PL должен иметь метод для вычисления цели. Результат вычисления может быть как одиночное значение, так и выборка. Для работы с последней предусмотрены методы итерации (EOF, First, Next) и методы для обращения к параметрам по имени.
  7. С помощью соответствующих методов результат вычисления может быть напрямую помещен в указанную таблицу в базе, клиент-датасет в оперативной памяти, или на его основе могут быть созданы новые бизнес-объекты в БД.
  8. Предикаты, которые создает разработчик, должны храниться в базе данных аналогично тому, как сейчас хранится код VBScript.
  9. В дереве Проводника Редактора скрипт-объектов следует добавить корневой раздел для кода на Прологе. Внутри раздела пролог-скрипты могут размещаться в папках.
  10. Минимальная единица для загрузки в объект PL -- один пролог-скрипт. Каждый скрипт имеет уникальное имя и РУИД. Загрузка осуществляется по РУИДу. Внутри скрипта могут встречаться директивы %#INCLUDE для загрузки других пролог-скриптов (аналогично директиве '#INCLUDE для VBScript).
  11. Стандартные библиотеки SWI-Prolog, которые в дистрибутиве занимают 8 Мб в 377 файлах, будут храниться в базе данных точно так же, как и обычные пролог-скрипты.
  12. Отладка:
    1. Для отладки на компьютере должен быть установлен полный вариант SWI-Prolog и, возможно, SWI-Prolog Editor.
    2. Местоположение SWI-Prolog и каталога для временных файлов задается в параметрах платформы.
    3. Режим отладки активизируется в параметрах редактора скрипт-объектов для списка конкретных экземпляров PL, заданных их именами. Если список пуст, то режим активируется для всех PL объектов.
    4. Когда активирован режим дебаггера предикаты записываются в текстовые файлы .pl программ во временном каталоге. При этом файлы формируются следующим образом:
      • Для каждого пролог-скрипта отдельный файл.
      • Результаты вызова MakePredicatesX помещаются в файлы в соответствии с заданными именами.
    5. При вызове метода вычисления цели в режиме дебаггера загружается SWI-Prolog или SWI-Prolog Editor. В среду выполнения/отладки подгружаются созданные временные файлы.
    6. Гедымин ждет завершения работы процесса SWI-Prolog.
    7. После окончания отладки проверяются файлы для выгруженных пролог-скриптов. Если обнаружены изменения Гедымин предлагает загрузить их в базу данных.

8 авг. 2013 г.

Первые шаги в функциональном программировании

Двадцать лет назад я собирался изучить Prolog. Купил даже книгу Ивана Братко "Программирование на языке Пролог для искусственного интеллекта". Так бы она и пылилась на полке, если бы не Гедымин и очередной наш технологический прорыв на стыке пост-реляционных технологий и функционального программирования.

А вот и собственноручно написанный код. Решение первой задачи Проекта Ойлера:

my_sum(From, To, C, C2, S) :-
  From < To,
  ( From mod C =:= 0; From mod C2 =:= 0 ),
  Next is From + 1,
  my_sum(Next, To, C, C2, T),
  S is T + From.
my_sum(From, To, C, C2, S) :-
  From < To,
  From mod C =\= 0,
  From mod C2 =\= 0,
  Next is From + 1,
  my_sum(Next, To, C, C2, T),
  S is T.
my_sum(From, To, _, _, S) :-
  From = To,
  S is 0.
PS: Надо же: Леонард Ойлер оказывается родился в Базеле ))