"ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ ПРОЛОГ" - читать интересную книгу автора (Клоксин У, Меллиш К)

1.5. Правила

Предположим, мы хотим сформулировать утверждение, что Джону нравятся все люди. Один из способов сделать это заключается в записи для каждого человека, упоминаемого в базе данных, отдельного факта:


нравится(джон,альфред).

нравится(джон,бертран).

нравится(джон,чарлз).

нравится(джон,дейвид).

* * *


Это было бы очень утомительным, особенно если в нашей программе на Прологе упоминается несколько сот человек. Другой способ выразить факт, что Джону нравятся все люди, - это сказать Джону нравится любой объект при условии, что этот объект является человеком. Здесь этот факт представлен в форме правила для определения того, что нравится Джону, а не прямого перечисления всех людей, которые ему нравятся. В ситуации, когда Джону мог бы нравиться любой человек, представление утверждения в виде правила является значительно более компактным, чем список фактов.

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

Я пользуюсь зонтом, если идет дождь.

Джон покупает вино, если оно дешевле, чем пиво.

Правила используются также для выражения определений, например:

X является птицей, если:

 X является живым существом и X имеет перья.

или

X является сестрой Y, если:

 X является женщино и X и Y имеют одних и тех же родителей.

В последних примерах мы использовали переменные X и Y. Важно помнить, что каждое вхождение переменной в правило обозначает один и тот же объект. Иначе мы разрушили бы саму суть определения. Например, используя приведенное выше определение птицы, мы не смогли бы показать, что Фред является птицей на основании того, что Фидо – это живое существо, а Мэри имеет перья. Этот принцип согласованной интерпретации переменных справедлив также и для правил в Прологе.

Правило – это некоторое общее утверждение об объектах и об отношениях между ними. Например, мы можем сказать, что Фред является птицей, если Фред является живым существом и Фред имеет перья, мы можем также сказать, что Бертрам является птицей, если Бертрам является живым существом и Бертрам имеет перья. Таким образом, мы допускаем, что при каждом новом использовании правила переменная обозначает новый, отличный от прежнего объект. Конечно, в рамках конкретного использования правила переменные интерпретируются согласованно, как на это указывалось выше.

Рассмотрим несколько примеров, начав с правила, содержащего одну переменную и конъюнкцию:

Джону нравится любой, кому нравится вино, или,

другими словами,

Джону нравится что-то, если чему-то нравится вино,

или, используя переменные,

Джону нравится X, если X нравится вино.

В Прологе правило состоит из заголовка и тела правила. Заголовок и тело соединяются с помощью символа :-, который состоит из двоеточия : и тире -. Символ ':-' читается если.

Предыдущий пример записывается на Прологе следующим образом:


нравится(джон,X):- нравится(Х,вино).


Отметим, что правила также заканчиваются точкой. Заголовком этого правила является нравится(джон,Х). Заголовок правила описывает факт, для определения которого предназначено это правило. Тело правила, в данном случае нравится(Х,вино), описывает конъюнкцию целей, которые должны быть последовательно согласованы с базой данных, для того чтобы заголовок правила был истинным. Например, мы можем сделать Джона более разборчивым в выборе тех, кто ему нравится, просто добавив к телу правила еще несколько целевых утверждений, разделив их запятыми:


нравится(джон,X):- нравится(Х,вино), нравится(X,пища).


или, другими словами, Джону нравится любой, кому нравятся вино и пища. Или, предположим, что Джону нравится любая женщина, которой нравится вино:


нравится(джон,Х):- женщина(Х), нравится(Х,вино).


Всякий раз, когда мы имеем дело с правилом в Прологе, необходимо отмечать все вхождения переменных. В последнем примере переменная X использована три раза. Всякий раз, как переменная X конкретизируется некоторым объектом (ей присваивается значение), все вхождения X в пределах области действия этой переменной становятся конкретизированными. При каждом употреблении правила область действия переменной X – это все правило, начиная с заголовка и до точки '.' в конце этого правила. Так, если в приведенном выше правиле переменная X оказалась конкретизированной, принимая значение мэри, то Пролог попытается согласовать с базой данных целевые утверждения женщина(мэри) и нравится(мэри,вино).

Теперь, чтобы продемонстрировать правило, использующее более одной переменной, рассмотрим базу данных, содержащую факты о семействе королевы Виктории. Мы будем использовать предикат родители, имеющий три аргумента. родители(Х, Y, Z) означает: Родителями X являются Y и Z. Переменная Y обозначает мать, а переменная Z обозначает отца. Кроме того, мы будем использовать предикаты женщина и мужчина в их очевидном значении. Некоторая часть этой базы данных могла бы выглядеть следующим образом:


мужчина(альберт).

мужчина(эдуард).

женщина(алиса).

женщина(виктория).

родители(эдуард,виктория,альберт).

родители(алиса,виктория,альберт).


Здесь мы воспользуемся описанным ранее правилом является_сестрой. Правило определяет предикат является_сестрой, имеющий два аргумента, таким образом, что является_сестрой(X, Y) истинно, если X является сестрой Y. Обратим внимание на использование в имени предиката символа подчеркивания '_'. Хотя до сих пор не было дано полных правил конструирования имен, отметим, что допускается использование подчеркивания в именах, а более подробно об этом будет сказано в следующей главе. Тогда X является сестрой Y, если:

• X является женщиной,

• X имеет мать М и отца F и

• Y имеет тех же мать и отца, что и X.

Это можно записать в виде следующего правила Пролога:


является_сестрой(X,Y):- женщина(X), родители(X,M,P), родители(Y,M,P).


Мы используем переменные M и F для обозначения матери и отца, хотя при желании мы могли бы использовать имена Мать и Отец. Отметим, что мы употребляем переменные, которые не появляются в заголовке правила. Эти переменные, M и F, обрабатываются таким же образом, как и любая другая переменная. Когда Пролог использует это правило, переменные M и F изначально будут неконкретизированными. Этим переменным будет присвоено некоторое значение в момент установления соответствия для предиката родители(X,M,F). Однако, как только они конкретизируются, становятся конкретизированными и все вхождения переменных M и F, соответствующие текущему использованию правила. Следующий пример должен помочь объяснить, как используются эти переменные. Давайте зададим вопрос:


?- является_сестрой(алиса,эдуард).


Имея описанные выше базу данных и правила является_сестрой и получив такой вопрос, Пролог выполняет следующие действия:

1. Сначала вопрос сопоставляется с единственным правилом для предиката является_сестрой, приведенным выше. При этом переменная X конкретизируется, принимая значение алиса, и переменная Y конкретизируется значением эдуард. Правило, с которым произошло сопоставление, отмечается маркером. Теперь Пролог пытается последовательно согласовать с базой данных три предиката, входящие в тело правила.

2. Так как на предыдущем шаге переменной X присвоено значение алиса, то первой целью является женщина(алиса). Истинность этого предиката следует из списка фактов, так что цель достигнута. Поскольку данная цель согласована, то Пролог отмечает соответствующее ей место в базе данных (третье утверждение в базе данных). При этом не произошло никаких присвоений значений переменным. Далее Пролог пытается согласовать следующую цель.

3. Теперь Пролог ищет соответствие для предиката родители(алиса,M,F), где переменные M и F сопоставимы с любыми аргументами, так как первоначально они неконкретизированы. Факт, с которым происходит сопоставление, есть родители(алиса, виктория,альберт), и тем самым вторая цель достигнута. Пролог отмечает маркером соответствующее место в базе данных (шестое утверждение сверху) и записывает, что M присвоено значение виктория, a F – значение альберт. (Если хотите, вы можете делать соответствующую запись над целевым утверждением в правиле.) Затем Пролог пытается найти соответствие для следующего предиката в правиле.

4. Теперь Пролог ищет в базе данных факт родители(эдуард,виктория,альберт), так как из запроса нам известно, что Y – это эдуард, а из предыдущего шага мы знаем, что M и F обозначают виктория и альберт. Эта цель достигается, поскольку найден подходящий факт (пятое утверждение сверху). Так как это последняя цель в конъюнкции, то и полное целевое утверждение является согласованным с базой данных, и тем самым доказано, что факт является_сестрой(алиса, эдуард) является истинным, Пролог отвечает да.

Предположим, мы хотим знать, является ли Алиса чьей-либо сестрой. Соответствующий вопрос на Прологе имеет вид


?- является_сестрой(алиса,X).


В ответ на вопрос Пролог выполняет следующие действия:

1. Вопрос сопоставляется с заголовком единственного правила для предиката является_сестрой. Переменная X, входящая в это правило, конкретизируется значением алиса. Так как переменная X в запросе неконкретизирована, то и переменная Y в правиле также будет неконкретизированной. Однако эти две переменные теперь становятся сцепленными. Как только одной из переменных присваивается некоторое значение, другая переменная становится конкретизированной тем же самым значением. Конечно, в данный момент они неконкретизированы.

2. Первая цель – женщина(алиса), которая достигается так же, как и в предыдущем примере.

3. Вторая цель – родители(алиса,М,F). Эта цель сопоставляется с родители(алиса,виктория,альберт). Переменные M и F становятся конкретизированными.

4. Так как переменная Y пока неизвестна, то третьей целью будет родители(Y,виктория,альберт), и она сопоставляется с родители(эдуард, виктория,альберт). Переменная Y конкретизируется значением эдуард.

5. Так как все целевые утверждения согласованы с базой данных, то тем самым согласовано и правило в целом, при этом переменная X (как известно из вопроса) равна алиса и Y равна эдуард. Учитывая, что Y (в правиле) является сцепленной с X (в вопросе), то X также конкретизирована значением эдуард. Пролог печатает Х=эдуард.

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

Как мы видели до сих пор, существуют два способа предоставить Прологу информацию относительно предиката, подобного предикату нравится. Мы можем сделать это, используя как факты, так и правила. В общем случае предикат будет определен смесью фактов и правил. Эти факты и правила, определяющие предикат, называются утверждениями[4]. Мы будем использовать слово утверждение в случаях, когда мы ссылаемся либо на факт, либо на правило.

В качестве следующего примера, на этот раз не имеющего отношения к монархам, рассмотрим правило: Человек может украсть что-либо, если этот человек вор и ему нравится вещь и эта вещь является ценной. На Прологе это записывается следующим образом:


может_украсть(P,T:- вор(P), нравится(P,T), ценный(T).


Предикат может_украсть, который имеет две переменные P и T, представляет отношение: некоторый человек P может украсть вещь T. Это правило зависит от утверждений, определяющих предикаты вор, нравится и ценный. Они могут быть представлены либо как факты, либо как правила в зависимости от того, что является более подходящим. Например, рассмотрим следующую базу данных, составленную в том числе из утверждений, обсуждавшихся ранее. Мы добавим к ним номера утверждений, заключенные между специальными скобками /*… */. Именно таким образом в Пролог-системе записывается комментарий. Комментарии игнорируются Пролог-системой, но мы можем добавить их в программу для удобства. В последующем обсуждении мы будем ссылаться на номера предложений, представленные в виде комментариев.

/*1*/ вор(джон).

/*2*/ нравится(мэри, пища).

/*3*/ нравится(мэри,вино).

/*4*/ нравится(джон,X):- нравится(X,вино).

/*5*/ может_украсть(X,Y):- вор(X), нравится(X,Y).

Отметим, что определение предиката нравится содержит три отдельных утверждения: два факта и правило. Для Пролога это не имеет значения. Единственное различие состоит в том, что когда осуществляется поиск в базе данных, чтобы согласовать с ней некоторую цель, то правило вызывает дальнейший поиск, чтобы согласовать его собственные предикаты-подцели. Факт не имеет подцелей, так что при сопоставлении с фактом поиск либо сразу завершается, либо сразу происходит переход к следующему утверждению. Например, давайте проследим за тем, что получится, если обратиться к Прологу с вопросом: Что Джон может украсть? Прежде всего этот вопрос транслируется на Прологе:

?- может_украсть(джон,Х).

Чтобы ответить на этот вопрос, Пролог осуществляет поиск следующим образом:

1. Прежде всего Пролог ищет в базе данных утверждение, описывающее предикат может_украсть, и находит такое утверждение. Оно представлено в виде правила и имеет номер 5. Пролог отмечает это место в базе данных. Так как это утверждение является правилом, то, чтобы установить, согласуется ли заголовок правила с базой данных, необходимо попытаться согласовать с ней тело правила. Тогда переменной X в правиле 5 присваивается значение джон, которое берется из вопроса. Как и в предыдущих примерах, мы должны сопоставить неконкретизированные переменные (X в вопросе и Y в правиле), так что теперь они будут сцеплены. Если вы не уверены, что до конца понимаете, что это значит, то необходимо вернуться назад к примерам с предикатом является_сестрой(X, Y). Для того чтобы правило выполнилось, необходимо согласовать цели с базой данных. Таким образом, теперь проверяется на согласованность с базой данных первое утверждение вор(джон).

2. Эта цель достигается, так как факт вор(джон) содержится в базе данных (утверждение 1). Пролог отмечает это место в базе данных, и при этом присвоения значений переменным не происходит. Далее Пролог пытается достигнуть вторую цель, применяя утверждение 5. Так как X, как и ранее, обозначает джон, то теперь Пролог ищет нравится (джон, Y). Заметим, что к этому моменту Y остается неконкретизированной.

3. Цель нравится(джон, Y) сопоставляется с заголовком правила (утверждение 4). Переменная Y, входящая в цель, сцепляется с X в заголовке правила, и обе эти переменные остаются неконкретизированными. Чтобы доказать это правило, теперь ищется нравится(Х, вино).

4. Эта цель достигается, так как она сопоставляется с нравится (мэри,вино) - фактом, являющимся утверждением с номером 3. Так что теперь X становится мэри.

5. Так как цель в утверждении 4 достигнута, то согласовано и правило в целом. Факт нравится(джон, мэри) следует из утверждения 4, так как переменная Y в утверждении 5 сцеплена с X, и ей тоже присваивается значение мэри.

6. Утверждение 5 теперь согласуется с базой данных при Y, имеющем значение мэри. Так как переменная Y была сцеплена со вторым аргументом исходного вопроса, то переменная X в вопросе конкретизируется, принимая значение мэри. Приведем рассуждение, обосновывающее факт Джон может украсть Мэри:

Для того чтобы украсть что-либо, прежде всего Джон должен быть вором. Из утверждения 1 следует, что это имеет место. Далее, Джону должен нравиться похищаемый предмет. Из утверждения 4 мы видим, что Джону нравится любой, кому нравится вино. Из утверждения 3 мы видим, что Мэри нравится вино. Следовательно, Джону нравится Мэри. Поэтому оба условия для похищения некоторого объекта имеют место, а значит, Джон может украсть Мэри.

Заметим, что факт (утверждение 2) о том, что Мэри нравится пища, не имеет никакого отношения к данному конкретному запросу, так как он нигде не понадобился.

В приведенном примере мы повторно использовали переменные X и Y в различных утверждениях. Например, в правиле может_ украсть X обозначает объект, который может что-нибудь украсть. Но в правиле нравится X обозначает объект, которому что-то нравится. Для того чтобы приведенная программа имела смысл, в Прологе должна иметься возможность указывать, что X может обозначать различные вещи в различных употреблениях утверждений. Помните, что знание области действия переменной может разрешить любые неясности. Мы могли бы использовать более мнемоничные имена, чтобы попытаться предотвратить любые неясности, но мы используем простые имена, такие как X, чтобы продемонстрировать работу принципа области действия переменной.