Язык Пролог: вычисления и процедурная семантика Пролога
Вычисления и процедурная семантика Пролога
Итак, выполнение программы Пролога заключается в интерпретации значений переменных, при которой все правила становятся истинными. Если такой интерпретации нет, то ответ Нет. Если правила программы истинны при любой интерпретации, то ответ Да. В остальных случаях ответами являются все всевозможные подстановки переменных, при которых все правила истинны.
Каждую формулу Пролога (запрос, факт, правило) можно рассматривать как процедуру . При этом левая часть правила - вход в процедуру , а правая часть - тело процедуры , которая может содержать вызовы других процедур. Атомарные предикаты запросов можно также понимать как вызовы процедур .
Мы будем описывать алгоритм вычислений, одновременно рассматривая и поясняя его на примере.
<- предок (X, петр)
для программы предыдущего раздела.
1. В вычислениях каждый атом правой части запроса рассматривается как вызов процедуры. Если в запросе несколько атомов, то эти атомы рассматриваются по очереди, и если переменные очередного атома не имеют ни одной интерпретации, ведущей к ответу для этой части запроса, то и весь исходный запрос не имеет ответа для интерпретации переменных, полученной в вычислениях предыдущих атомов. Если же есть интерпретации для переменных атома, ведущие к ответу, то только такие интерпретации переменных используются в частях запроса для следующих атомов. В данном запросе 1 вызов процедуры предок.
2. При вызове процедуры мы по очереди рассматриваем формулы (правила или факты) этой процедуры.
Шаг 1. В данном примере для процедуры предок мы берем сначала первое правило для этой процедуры, но делаем регулярную замену переменных:
предок (X1, Y1) <- мать (X1, Y1).
Вызов процедуры предок ( X, петр ) совпадает с заголовком этого правила при интерпретации: X=X1, Y1= петр.
3. Производим интерпретацию переменных заголовка (правила или левой части факта) и заменяем прежний атомарный запрос на запрос тела процедуры, в котором произведена полученная интерпретация переменных.
4. Если интерпретация невозможна, то необходимо перейти к предыдущей точке выбора формулы процедуры. Если при этом нет предыдущей точки выбора, то вычисления закончились неуспешно.
<- мать (X1, петр).
Шаг 2. С этим запросом поступаем аналогичным образом, но так как процедура мать представляет собой множество фактов, то рассматриваем лишь такие факты
мать(X2, Y2) <-,
которые допускают интерпретацию Y2 = петр. Так как таких нет, то вычисление закончилось неудачно и нужно вернуться к предыдущей точке алгоритма, где мы можем сделать следующий выбор. Такой точкой является шаг 1, где мы выбирали правило процедуры предок.
Шаг 3. Выбираем следующее правило процедуры \unde предок с заменой переменных:
предок (X1, Y1) <- отец (X1, Y1).
Вызов процедуры предок ( X, петр ) совпадает с заголовком этого правила при интерпретации: X1=X, Y1 = петр. Заменяем исходный запрос на тело процедуры с полученной интерпретацией переменных и получаем новый запрос:
<- отец (X1, петр).
Шаг 4. С этим запросом поступаем аналогичным образом, но так как процедура отец представляет собой множество фактов, то рассматриваем лишь такие факты
отец (X2, Y2)<-,
которые допускают интерпретацию X2 = X1, Y2 = петр. Есть 1 такой факт
отец (иван, петр) <-,
который дает интерпретацию X2=иван.
5. Как только замена заголовка процедуры на тело дает пустой запрос, который является истинным, вычисления исходного запроса (атома) заканчиваются успешно.
Делая обратные подстановки иван = X2 = X1 = X, получаем первый ответ X=иван.
Теперь необходимо вернуться к предыдущей точке выбора формулы.
Шаг 5. В процедуре предок выбираем следующее правило с заменой переменных:
предок (X1, Y1) <- предок (Z1, Y1), мать (X1, Z1).
Вызов процедуры предок (X, петр) совпадает с заголовком этого правила при интерпретации: X1=X, Y1= петр. Заменяем исходный запрос на тело процедуры с полученной интерпретацией переменных и получаем новый запрос:
<- предок (Z1,петр), мать (X1, Z1).
Мы получили запрос с 2 атомами и должны рассматривать их по очереди.
Шаг 6. Рассматриваем запрос с первым атомом
<- предок (Z1,петр) В процедуре предок выбираем первое правило (первое, потому что это новый запрос в нашем вычислении) с заменой переменных:
предок (X1, Y1) <- мать (X1, Y1).
Вызов процедуры предок (X, петр) совпадает с заголовком этого правила при интерпретации: Z1=X1,Y1=петр. Делая замену на тело этой процедуры (с интерпретацией переменных), получаем запрос:
<- мать (X1, петр)
такой же, как и на шаге 2, вычисления которого закончились неудачно. Поэтому выбираем следующее второе правило для процедуры предок:
предок (X1, Y1) <- отец (X1, Y1).
Вызов процедуры предок (X, петр) совпадает с заголовком этого правила при интерпретации: X1=Z1,Y1=петр. Заменяем исходный запрос на тело процедуры с полученной интерпретацией переменных и получаем новый запрос:
<- отец (X1, петр).
Шаг 7. С этим запросом поступаем аналогичным образом, но так как процедура отец представляет собой множество фактов, то рассматриваем лишь такие факты
отец (X2, Y2) <-,
которые допускают интерпретацию X2=X1, Y2=петр. Есть 1 такой факт
отец (иван, петр) <-,
который дает интерпретацию X2 = иван. Вычисления запроса для первого атома шага 5 закончились успешно, и мы получаем интерпретацию Z1 = иван, которую используем во втором атоме запроса, рассматриваемом на следующем шаге.
Шаг 8. Рассматриваем второй атом запроса с интерпретацией переменной Z1:
<- мать (X1, иван).
Так как процедура мать представляет собой множество фактов, то рассматриваем лишь такие факты
мать(X2, Y2) <-,
которые допускают интерпретацию X2 = X1, Y2 = иван. Есть 1 такой факт
мать (елена, иван) <-,
который дает интерпретацию X2 = елена. Так как это факт, то вычисления второго атома запроса также закончились успешно. В результате получаем елена = X2=X1=X, и, следовательно, второй ответ X = елена.
Теперь для завершения вычислений нужно вернуться к предыдущей точке выбора и рассмотреть последнее правило процедуры предок:
предок (X1, Y1) <- предок (Z1, Y1), отец (X1, Z1).
Мы опять получили запрос с 2 атомами и должны рассматривать их по очереди.