Факты и база знаний

Урок о том, как в Prolog записывают знания о мире — через факты — и из чего складывается база знаний.

Факт — это безусловное утверждение Prolog о том, что некоторое отношение между объектами истинно.

В обычных языках программа описывает, как вычислять результат: завести переменную, сложить, записать в массив. В Prolog мысль другая: мы описываем мир — какие объекты существуют и в каких отношениях они состоят. Самый простой способ что-то рассказать системе — записать факт. Факт ничего не вычисляет; он просто заявляет: «вот это — правда».

Представьте, что вы рассказываете другу о семье. Вы говорите: «Том — родитель Боба». Это законченное утверждение об отношении между двумя людьми. В Prolog ровно та же мысль выглядит так:

parent(tom, bob).

Здесь parent — это предикат (имя отношения), а tom и bob — его аргументы (участники отношения). Точка в конце обязательна: она говорит, что утверждение закончено. Прочитать запись можно так: «tom является родителем bob».

Зачем нужны факты

Факты — это сырьё, на котором держится всё остальное в Prolog. Без них системе нечего знать о вашем мире, и любой запрос даст «ложь». Запросы (мы изучим их в следующем уроке) ищут ответы среди фактов; правила (третий урок) выводят новые знания, опираясь на факты. Поэтому грамотно описанные факты — половина успеха.

Важно понять: Prolog не «понимает», что такое родитель, ребёнок или дружба. Слово parent для него — просто метка. Смысл вкладываете вы, выбирая осмысленные имена и согласованный порядок аргументов. Если вы решили, что parent(Родитель, Ребёнок), то держитесь этого порядка во всей базе, иначе запросы начнут путать роли.

Синтаксис факта по частям

Разберём анатомию факта parent(tom, bob). на составляющие:

ЧастьПримерЧто это
Предикатparentимя отношения
Аргументыtom, bobобъекты в скобках через запятую
Арность2число аргументов
Точка.конец утверждения

Число аргументов называют арностью и записывают как parent/2. Это важно: parent/2 (двухместный) и parent/1 (одноместный) — для Prolog два совершенно разных предиката, даже если имя одно.

Имена-атомы со строчной буквы

Имена объектов вроде tom, bob, ann — это атомы, неделимые символьные константы. Ключевое правило: атом начинается со строчной буквы. Если написать Tom с заглавной, Prolog решит, что это переменная (о них — в уроке про запросы), и смысл сломается. Запомните пока просто: имена людей пишем с маленькой буквы.

% правильно: атомы со строчной буквы
parent(tom, bob).
parent(ann, bob).

% Tom и Bob с заглавной — это переменные, не имена!

Если имя обязательно должно начинаться с большой буквы или содержать пробел, его берут в одинарные кавычки: 'Tom Sawyer'. Но для учебных примеров проще придерживаться коротких строчных атомов.

Собираем базу знаний

Несколько фактов вместе образуют базу знаний — это и есть программа на Prolog. Опишем небольшую семью:

parent(tom, bob).
parent(tom, liz).
parent(bob, ann).
parent(bob, pat).
parent(pat, jim).

male(tom).
male(bob).
male(jim).

female(liz).
female(ann).
female(pat).

Здесь два разных отношения. parent/2 связывает родителя и ребёнка, а male/1 и female/1 приписывают человеку пол — это уже одноместные предикаты, утверждающие свойство одного объекта. Вместе они описывают маленький мир: кто кому родитель и какого кто пола.

Полезно представить эту базу как дерево связей:

tom
├── bob
│   ├── ann
│   └── pat
│       └── jim
└── liz

Заметьте: Prolog ничего не знает о «бабушках» и «сёстрах» — пока в базе лишь прямые отношения «родитель — ребёнок». Вывести бабушку он сможет, но для этого понадобятся правила.

Как работает под капотом

Когда вы загружаете файл с фактами, Prolog не выполняет их подряд, как операторы императивного языка. Он складывает их в специальную внутреннюю структуру — базу данных предикатов. Каждый факт становится записью (clause) под своим предикатом. Все факты parent/2 ложатся в одну «стопку» в том порядке, в котором записаны в файле.

Этот порядок не безразличен: позже, отвечая на запрос, Prolog будет просматривать факты сверху вниз, от первого к последнему. Поэтому parent(tom, bob) будет найден раньше, чем parent(tom, liz). Сами по себе факты — это пассивные данные; «оживают» они только когда приходит запрос и запускается механизм поиска.

Ещё одна деталь: факт — это частный случай правила без условий. В следующих уроках вы увидите запись head :- body; так вот, факт parent(tom, bob). — это то же самое, что правило «parent(tom, bob) истинно, если ничего не требуется». Тело пустое, поэтому факт всегда безусловно верен.

Частые ошибки

  • Забытая точка. Без точки в конце Prolog считает, что факт не закончен, и «склеивает» его со следующей строкой — появляется загадочная синтаксическая ошибка.
  • Заглавная буква в имени. parent(Tom, bob).Tom читается как переменная, и факт получает совсем не тот смысл, что вы задумали.
  • Пробел перед скобкой. Между именем предиката и открывающей скобкой не должно быть пробела: пишите parent(tom, bob), а не parent (tom, bob).
  • Разный порядок аргументов. Если в одних фактах parent(Родитель, Ребёнок), а в других наоборот, база становится противоречивой, и запросы возвращают бессмыслицу.
  • Путаница арности. parent(tom) и parent(tom, bob) — разные предикаты; запрос к одному не увидит факты другого.

Итоги

  • Факт — безусловное утверждение, что отношение истинно: parent(tom, bob).
  • Предикат — имя отношения, аргументы в скобках — его участники, точка завершает факт.
  • Имена объектов — это атомы со строчной буквы; с заглавной начинаются переменные.
  • Арность (число аргументов) — часть «личности» предиката: parent/2parent/1.
  • Набор фактов образует базу знаний — это и есть программа на Prolog.
Проверьте себя
1. Как правильно записать факт «Том — родитель Боба» в Prolog?
Aparent(Tom, Bob)
Bparent(tom, bob).
CParent(tom, bob).
Dparent tom bob.
2. Почему имя tom пишут со строчной буквы?
AТак требует орфография английского
BСлово с заглавной буквы Prolog воспримет как переменную, а не как имя объекта
CЗаглавные буквы запрещены в Prolog вообще
DЭто лишь рекомендация по стилю, смысла не меняет
3. Что такое арность предиката parent/2?
AПорядковый номер факта в базе
BЧисло его аргументов — здесь два
CКоличество фактов parent в базе
DДлина имени предиката