Два правила хорошего стиля программирования на Прологе. Декларативная граница

Первое правило таково:

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

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

Второе правило состоит в следующем:

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

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

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

Примеры декларативной границы

Рассмотрим несколько примеров декларативных границ. Пусть задача состоит в том, чтобы написать фразу, которая определяет лицо М как мать-одиночку лица Р (ребенка), если М — мать Р, М живет с Р, М разведена и М не состоит в повторном браке. Предположим, что база данных "мать" очень велика. Нужно добавить в правило такую управляющую информацию, чтобы интерпретатор не терял понапрасну время на поиск более чем одного значения М при обработке запроса к правилу, в котором переменная Р конкретизирована, а переменная М не конкретизирована.

Первое решение задачи

% Одна Много

мать—одиночка! (М, Р) : —

nonvar(Р),

мать(М,Р),

!, % у Р только одна мать, поэтому запретить возврат назад.

живет_с(М,Р),

разведена (М),

not (повторно—замужем (М) ).

мать_одиночка! (М,Р) :—

var (Р),

мать(М,Р),

живет_с (М,Р),

разведена (М),

not (повторно_замужем (М) ) .

мать (сюзан, бобби).

мать (кетлин, фред).

мать (исадора, Мишель).

мать(барбара, венди).

мать(барбара,стив).

живет_с (сюзан, бобби).

живет_с (исадора, Мишель).

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

живет_с (барбара, стив).

разведена (кетлин).

разведена(исадора).

разведена(барбара).

повторно_замужем (кетлин).

Для проверки того, правильно ли процедура "мать_одиночка1" решает сформулированную выше задачу, воспользуйтесь отладочными средствами Вашего интерпретатора при выполнении запроса:

| ?— мать_одиночка (Кто, венди).

Кто = барбара;

нет

Выходная информация, выдаваемая отладочными средствами, покажет, что интерпретатор не возвращается назад для поиска второй матери Венди.

В приведенном первом решении декларативная граница расположена на уровне процедуры "мать_одиночка1". Реализация процедуры "мать_одиночка1 " не обладает декларативным смыслом, так как в ней употребляются побочные эффекты. Однако вызов процедуры "мать_одиночка1" имеет декларативный смысл. Лучше всего рассматривать процедуру "мать_одиночка1" как процедуру, которая выполняет все действия, необходимые для установления отношения "мать_одиночка1" между своими аргументами, причем делает это эффективно.

Второе решение задачи

мать_одиночка2(М,Р) :—

ом_мать (М, Р),

живет_с(М,Р),

разведена (М),

not (повторно_замужем (М) ).

% промежуточная процедура доступа к базе данных "мать", реализую-

% щая ограничение вида один-к-многим:

% Одна Много

ом_мать (М, Р) :—

nonvar(P),

мать(М,Р),!.

ом_мать (М, Р) :—

var (Р),

мать(М,Р).

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

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

Третье решение задачи

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

мать_одиночка3(М,Р) :-

мать(М,Р),

живет_с(М,Р),

разведена (М),

not (повторно—замужем (М) ).

% объявить свойство "один-к-многим" отношения "мать":

% Отношение Одна Много

один—много (мать (Мать, Ребенок), Мать, Ребенок).

% решатель задач, который знает о существовании отношений вида

% один-к-многим":

рз (true) : — !.

рз ( (А, Б) : - % составной запрос

!,

рз(А),

рз(Б).

рз (А) : — % особый случай: А — это отношение вида

% "один-к-многим"

один-много (А, X, Y),

!,

ом_з(А,Х,Y).

рз (А) : — % А не является отношением вида "один-к-многим"

clause (А, Тело),

рз(Тело).

% эффективно обработать отношение вида "один-к-многим":

% Один Много

ом-рз(А,Х, Y) :-

nonvar(Y),

clause (А, Тело),

рз(Тело),!.

ом_рз(А,Х,Y) :-

var(Y),

clause (А, Тело),

рз(Тело).

Обратите внимание на сходство процедур "рз" (сокращение от словосочетания "решатель задач") и "вып0" (см. разд. 72). В процедуре "рз" есть дополнительная фраза, предназначенная для работы с запросами, которые регулируются ограничением, обеспечивающим целостность, вида один-к-многим. Процедура "ом_рз" очень похожа по форме на процедуру "ом_мать".

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

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

Декларативная управляющая информация

В результате дополнительных усилий, потребовавшихся на составление процедуры "рз", процедура "мать_одиночка3" теперь целиком располагается на дескриптивной стороне границы, и поэтому больше нет необходимости в использовании промежуточной процедуры, обеспечивающей доступ к базе данных "мать". Факт "один—много" также находится на дескриптивной стороне границы, так как он декларирует свойство другой части описания. Смысл этого факта находится на уровне метаязыка. Данный факт содержит декларативную управляющую информацию, которая используется процедурой "рэ".

Соседние файлы в папке Гл.6,7,Прилож.,Допол