3. 11. Преобразование процедурного алгоритма в программу на языке пролог Сравнение поиска с возвратом и рекурсии

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

Таблица 3. 1

Представление

Алгоритм

факты

поиск с возвратом

рекурсивная

структура

(например, список)

рекурсия

Цикл "пока"

Рассмотрим в качестве примера спецификацию программы, записанную на псевдокоде. Эта программа считывает записи "служащий/4" и суммирует все оклады служащих конкретного отдела. В каждой записи "служащий/4" содержатся поля с информацией об имени служащего, номере отдела, должности и окладе этого служащего. Спецификация этой программы имеет вид:

Пока не наступил конец базы данных "служащий/4",

считать запись;

если поле с номером отдела содержит заданный номер,

то добавить к итоговой сумме содержимое поля

"оклад".

При достижении конца базы данных напечатать итоговую сумму.

Рекурсия

Предположим, что база данных "служащий" представлена в виде списка записей:

% Имя Отд. Должность Оклад

[сл (брайен, 100, оператор, 20000),

сл (нэнси, 200, начальник, 71000),

сл (ральф, 100, менеджер, 71500),

сл (фред, 100, техник, 29000),

сл (сюзан, 300, программист, 35000)]

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

Ритог_окл ([], _, 0).

% Содержимое поля "отдел" совпадает с нужным номером

ритог_окл ([сл (_, 0тд, _, 0кл); R], Отд, Итог): —

ритог_окл (R, Отд, Текущ_сумма),

Итог is Окл+ Текущ_сумма.

% Содержимое поля "отдел" не совпадает с нужным номером.

ритог_окл ([сл (-, ХОтд, _, Окл) | R], Отд, Итог): -

Х0тд\==0тд,

ритог_окл (R, Отд, Итог).

Напишем запрос, который позволит вычислить итоговую сумму окладов, выплачиваемых в отделе № 100:

|?— ритог_окл ([сл (брайен, 100, оператор, 20000),

сл (нэнси, 200, начальник, 71000),

сл (ральф, 100, менеджер, 715 00),

сл (фред, 100, техник, 29000),

сл (сюзи, 300, программист, 35000)],

100, Итог).

Итог = 120500

Поиск с возвратом

Предположим, что имеется база данных, состоящая из фактов "служащий/4". Процедура "итог_окл", приводимая ниже, вычисляет итоговую сумму окладов, выплачиваемых в определенном отделе. В этой процедуре используется метод поиска с возвратом. Первое правило процедуры "итог_окл" осуществляет циклически выбор нового факта "служащий", удаляет старую текущую сумму, вычисляет новую текущую сумму и добавляет ее как факт "текущ_сумма". (Заметьте, что если бы текущая сумма вычислялась без употребления предиката "один_раз", то процедура могла бы возвратиться назад к предикату "retract" и удалить только что добавленный факт "текущ_сумма". Затем процедура бы зациклилась, переходя от предиката "assert" к предикату "retract" и обратно.) После того, как все факты "служащий" будут исчерпаны, первое правило потерпит неудачу. Следовательно, цель употребления первого правила "итог_окл" заключается только в том, чтобы оно оказывало побочные эффекты на текущую программу. Второе правило "итог_окл" просто-напросто выдает текущее значение последней итоговой суммы, содержащееся в факте "текущ_сумма", и устанавливает новое значение этой суммы, равное нулю.

% Имя Отд. Должность Оклад

служащий (брайен, 100, оператор, 20000).

служащий (нэнси, 200, начальник, 71000).

служащий (ральф, 100, менеджер, 71500).

служащий (фред, 100, техник, 29000).

служащий (сюзан, 300, программист, 35000).

текущ_сумма (0).

итог_окл (Отд, Итог): - % (1)

служащий (_, 0тд, _, Окл),

один_раз (retract (текущ_сумма (Сумма))),

Итог is Окл + Сумма,

assert (текущ_сумма (Итог)),

fail.

итог_окл (_, Итог):- % (2)

retract (текущ_сумма (Итог) ),

assert (текущ_сумма (0) ).

|? - итог_окл (100, Всего).

Всего =120500

Примечание: поскольку факт "текущ_сумма/1" циклически добавляется данной программой и удаляется ею, этот факт имеет некоторые черты глобальной переменной.

Соседние файлы в папке Гл.0,1,2,3,4,5,Предисловие