Скачиваний:
53
Добавлен:
01.05.2014
Размер:
565.76 Кб
Скачать

4.2. Абстрактный интерпретатор логических программ

Мы пересмотрим абстрактный интерпретатор, описанный в разд. 1.8, в свете алгоритма унификации. В результате будет построена полная вычислительная модель логических программ. Все ранее введенные понятия, такие, как редукция цели и протокол вычисления, имеют соответствующие аналоги в полной модели.

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

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

Для более формального описания вычислений введем несколько полезных понятий. Вычислением цели Q = Q программой Р называется, возможно бесконечная последовательность троек <Q,G,C> здесь Q,-конъюнктивная цель,G-цель,входящая в Q,C-предложениеAB…B,с таким переименованием, что новые символы переменных не встречаются в Q, Оj1.Для всех i > 0 цель Qявляется или результатом замены G, на тело С, в Q, и применения подстановки Q,наибольшего общего унификатора термов G, и А, заголовок С), или константой true,если G-единственная цель в Q, и тело С, пусто, или константой отказ если С,и заголовок С, не унифицируемы.

Цели BΘ называются ,производными от цели Gи правила С. Цель Gj = B, где B входит в тело предложения С, называетсяпорожденной (G, и С. Цель Gназывается предшественником любой порожденной ею цели. Две цели с общим предшественником называются родственными целями.

Протоколом вычисления (Q,G,C) логической программы называется последовательность пар <G>, где Θ,- подмножество н.о.у. Θ, полученного на i-й редукции и ограниченного переменными из G.

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

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

Пересмотренная версия интерпретатора приведена на рис. 4.2. Интерпретатор решает вопрос G с помощью программы Р. Результатом работы интерпретатора будет пример G, если найдено доказательство такого примера, или отказ, если в процессе вычисления возник отказ. Заметим, что интерпретатор может и отказаться закончить вычисления.

Вход: Логическая программа Р

цель G

Результат:GΘ, если это пример G, выводимый из Р, или отказ, если возник отказ

Алгоритм: Начальная резольвента равна входной цели G

Пока резольвента непуста, выполнить

Выбрать такую цель А из резольвенты и такое (переименованное)

предложение

А''В,... ,B, п0 из Р, что А и А' унифицируемы с н.о.у. Θ

(если нет таких цели и правила, то выйти из цикла).

Удалить А и добавить В,... ,B, к резольвенте.

Применить Θ к резольвенте и к G.

Если резольвента пуста, то результат - G,

иначе результат-отказ.

Рис. 4.2. Абстрактный интерпретатор логических программ.

Пример вопроса, для которого найдено доказательство, называется решением вопроса.

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

Рассмотрим решение вопроса append([a,b],[c,d],Ls). относительно программы 3.15, задающей отношение append с использованием приведенного интерпретатора. Начальная резольвента - append([a,b],[c,d],Ls). Поскольку имеется единственная цель, то она и выбирается для редукции. Выбранным правилом будет

Append(X | Xs], Ys. [X | Zs])append(Xs. Ys.Zs].

Унификатор цели и заголовка {X= a.Xs = [b],Уs = [c,d],Ls = [a | Zs]}. Подробное описание нахождения унификатора приведено в предыдущем разделе. Новой резольвентой будет пример терма append {Xs,Ys.Zs} при применении унификатора, т.е.,append([b].[c,d],Zs). Эта цель выбирается на следующей итерации цикла. Выбирается то же предложение для отношения append, но во избежание конфликта переменные должны быть переименованы. Выберем такой вариант:

Append([Xl |Xsl], Ysl,[_Xl \ Zsl]) append(Xsl,Ys,ZsI).

Унификатор заголовка и цели -{Х1 = b, Xs1 = [ ], Ys1 = [c,d],Zs = [b \ Zsl]}. Новая резольвента - append ([ ],[c,d], Zs1}. Далее выбирается факт append([ ],Zs2.Zs2};вновь производятся необходимые переименования переменных. На этот раз унификатор - {Zs2 = [c,d],Zs1 =[c,d]}. Новая резольвента пуста, и вычисление заканчивается.

Для получения результата вычислений применим соответствующую часть вычисленного н. о. у. Первая унификация привела к изменению Ls на [a | Zs]. Во второй унификации Zs превратилось в [b\ Zs1]. Далее Zsl стало [c,d]. Объединяя, получим, что Ls имеет значение [а \ [b \ [с, d]]], или, упрощенно, [a,b, с, d].

Вычисление может быть представлено протоколом. Протокол описанного выше вычисления append приведен на рис. 4.3. Для того чтобы протокол был понятнее, цели напечатаны с отступом относительно предшественника. Цель имеет глубину отступа d+ 1, если глубина отступа предшественника -d.

append ([a,b],[c.d],Zs) Zs = | Zsl]

append([b],[c,d],Zs1) Zsl = [b | Zs2]

append([ ],[c,d],Zs2) Zsl = [c,d]

true

Output: Zs = [a,b,c,d]

Рис. 4.3. Протокол объединения двух списков.

В качестве другого примера рассмотрим решение вопроса сын(S,аран) в программе 1,2. Вопрос редуцируется с использованием предложения сын(Х,У) omeu(X,Y), мужчина (X). Наибольший общий унификатор – {X = S, Y = арап}. Применение подстановки дает новую резольвенту - отец(аран,S), мужчина(S). Это - конъюнктивная цель. В качестве очередной цели можно выбрать одну из двух целей. Выбор цели отец(аран,S) приводит к следующему вычислению. Цель унифицируется с фактом отец (аран, лот), имеющимся в программе, и вычисление продолжается с заменой S на лот. Новая резольвента -мужчина(лот), она является фактом программы, и вычисление завершается. Протокол вычисления приведен в левой части рис. 4.4.

сын (S.apaн) сын (S,apaн)

omeu(apaн,S) S = лот мужчина (S) S = лот

мужчина(лот) отец(аран,лот)

true true

Рис. 4.4. Различные протоколы, дающие одинаковое решение.

Другая возможность получить ответ S = лот состоит в выборе цели мужчина (S) раньше цели отец(аран,S). Эта цель редуцируется фактом мужчина(лот) с заменой S на лот. Новая резольвента - отец(аран, лот), которая с помощью соответствующего факта сводится к пустой резольвенте. Протокол этого вычисления приведен в правой части рис. 4.4.

Решение вопроса, полученное с помощью абстрактного интерпретатора, может содержать переменные. Рассмотрим вопрос member (а, Xs)? относительно программы 3.12. задающей отношение member. Его можно рассматривать как вопрос о том, какой список Xs содержит а в качестве элемента. Одно из решений, вычисленное абстрактным интерпретатором, - Xs = [а \Ys], т.е. список с головой а и произвольным хвостом. Решение, содержащее переменные, обозначает бесконечное множество решений - все их основные примеры.

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

Выбор цели для редукции произволен; для успешного вычисления несущественно, какая цель выбрана. Если существует успешное вычисление при выборе данной цели, то существует успешное вычисление и при выборе любой другой цели. Два протокола на рис. 4.4 описывают успешные вычисления, различающиеся выбором цели на втором шаге вычисления.

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

В некоторых вычислениях, например в вычислении, приведенном на рис. 4.3, на каждом шаге вычисления может быть использовано ровно одно предложение программы для редукции очередной цели. Такие вычисления называются детерминированными. Детерминированность вычисления означает, что нам не придется заниматься недетерминированным угадыванием.

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

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

Говоря формально, поиск в ширину определяет полную процедуру доказательства логических программ, а поиск в глубину - неполную процедуру. Несмотря на неполноту, в реализациях языка Пролог используется поиск в глубину, что объясняется практическими соображениями, рассматриваемыми в гл. 6.

Мы приведем протокол более длительного вычисления - вычисления программы 3.30 при решении задачи о ханойской башне с тремя дисками. Данное вычисление является детерминированным. Протокол вычисления приведен на рис. 4.5. Унификаторы к последним аррепd - целям не приводятся. Их запись не вызовет никаких затруднений.

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

hanoi(s(s(s(0))),a,b,c,Ms)

hanoi(s(s(0)),a,c,b,Ms1)

hanoi(s(0),a,b,c,Ms11)

hanoi(0,a,c,b,Ms111) Ms111=[ ]

hanoi (0,c ,b,a,Ms112) Ms112=[ ]

append([ ],[a to b],Ms11) Ms11=[a to b]

hanoi(s(0),b,c,a,Ms12)

hanoi(0,b,a,c,Ms121) Ms121=[ ]

hanoi(0,a,c,b,Msl22) Ms122=[ ]

append ([ ],[b to c],Ms12) Ms12=[b to c]

append([a to b],[a to c,b to c],Ms1) Ms1=[a to b | Xs]

append([ ],а to c,b to c],Xs) Xs=[a to c,b to c]

hanoi (s(s(0)) ,c ,b,a,Ms2)

hanoi (s (0) ,c ,a,b,Ms21)

hanoi (0,c,b,a,Ms211) Ms211=[ ]

hanoi(0,b,a,c,Ms212) Ms212=[ ]

append([ ],[c to a],Ms21) Ms21=[c to a]

hanoi(s(0),a,b,c,Ms22)

hanoi(0,a,c,b,Ms221) Ms221=[ ]

hanoi(0,c,b,a,Ms222) Ms222=[ ]

append ([ ],[a to b],Ms22) Ms22=[a to b]

append([c to a],[c to b,a to b],Ms2) Ms2=[c to a | Ys]

append([ ],[c to b,a to b],Ys) Ys=[c to b,a to b]

append([a to b,a to c,b to c],[a to b,c to a,c to b,a to b],Ms)

append([a to c,b to c],[a to b,c to a,c to b,a to b],Xs2)

append([b to c],[a to b,c to a,c to b,a to b],Xs3)

append([ ],[a to b,c to a,c to b,a to b],Xs4)

true

Результат: Ms=[a to b,a to c,b to c,a to b,c to a,c to b,a to b]

Рис. 4.5. Решение задачи о ханойской башне.

может быть соотнесена со многими механизмами таких языков размещением записей, назначением и организацией доступа к полям записи, передачей параметров и т. д. Мы отложим обсуждение этого вопроса до введения вычислительной модели языка Пролог в гл. 6.

Вычисление программы Р завершается, если G = true или G = отказ для некоторого п 0. Такое вычисление является конечным вычислением длины п. Успешные вычисления соответствуют завершающимся вычислениям, оканчивающимся константой true. Безуспешные вычисления оканчиваются константой отказ. До сих пор приводились протоколы успешных вычислений.

Рекурсивные программы допускают возможность бесконечных вычислений. Вопрос append(Xs[c,d],Ys), решаемый с помощью определения отношения append, допускает любое число редукций с использованием правила для отношения append. В процессе редуцирования переменной Xs сопоставляется список произвольной длины. Это соответствует решениям вопроса, присоединяющим [c,d] к произвольно длинному списку. Процесс вычисления показан на рис. 4.6.

Все до сих пор рассмотренные протоколы имели одно важное общее свойство - если две цели G и G порождены одним предшественником и цель G появляется в

append(Xs, [c ,d] ,Ys) Xs=[X|Xsl],Ys=[X|Ys]

append(Xsl,[c,d],Ysl) Xsl=[Xl | Xs2], Ysl=[Xl | Ys2]

append(Xs2, [c ,d] ,Ys2) Xs2=[X2 | Xs3], Ys2=[X2 | Ys3]

append(Xs3,[c,d],Ys3) Xs3=[X3 | Xs4], Ys3=[X3 | Ys4]

Рис. 4.6. Незавершающееся вычисление.

протоколе раньше цели G то все цели, порожденные G, появятся в протоколе раньше цели G. Такой метод расписания упрощает реализацию протоколов, поскольку решение вопросов выполняется в глубину.

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

Рассмотрим протокол вычисления на рис. 4.5.Цель

hanoi(s(s(s(0))),a,b,c,Ms)

сводится к следующей конъюнкции:

hanoi(s(s(0)),a,b,c,Ms1), hanoi(s(s(0) ),c,b,a, Ms2), append(Ms1,Ms2,Ms).

Если теперь выбрать цель append, то для ее редукции может быть неверно использован факт append. Применение редукции вначале к двум целям вида hanoi и ко всем порожденным ими целям приводит к цели append с правильными значениями для Ms1 и Ms2.

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

Соседние файлы в папке 1-13