- •Глава 3 управление ходом выполнения программы
- •3. 1. Как выполняется запрос в прологе
- •Три семантические модели
- •Выполнение запроса
- •Правила, описывающие унификацию термов
- •Пример с программой "дядя"
- •Активация запроса
- •Унификация запроса
- •Обработка тела фразы
- •Обработка подцели "брат"
- •Обработка подцели "отец"
- •Неудача запроса и возврат назад
- •Поиск третьего ответа
- •Поиск несуществующих ответов
- •Выполнение запроса к правилу "регистрация"
- •Автоматический просмотр выполнения программы
- •3.2. Предикат "сократить" Пространство поиска запроса
- •Предикат "сократить" останавливает возврат назад
- •Влияние предиката "сократить" на составной запрос
- •Влияние предиката "сократить" на процедуру
- •Предикат "сократить" как подцель в составном запросе
- •Использование предиката "сократить" для того, чтобы сделать процедуру детерминированной
- •Применение предиката "сократить" для отбрасывания части пространства поиска
- •Ограничение сферы действия предиката "сократить"
- •Общее правило ограничения сферы действия предиката "сократить"
- •3. 3. Отрицание как неудача запроса Негативная информация
- •Предположение о замкнутости мира
- •Тогда и только тогда, когда
- •Предположение об открытости мира
- •Отрицание в явной форме
- •Пример процедуры, поведение которой соответствует предположению об открытости мира
- •3. 4. Встроенные предикаты, предназначенные для обеспечения ввода-вывода Побочные эффекты ввода-вывода
- •Пример запроса, в котором используется предикат "read"
- •3. 5. Встроенные предикаты, предназначенные для управления файлами
- •3. 6. Проверка типа терма
- •Использование предикатов "var" и "nonvar"
- •3. 7. Действия с текущей программой
- •Изменение значения факта
- •Алгоритм поиска с возрастом, в котором используется предикат "assert"
- •Процедура, которая учится у пользователя
- •Программа, которая модифицирует сама себя
- •3. 8. Компараторы
- •3. 9. Прочие встроенные предикаты
- •Программа ввода "вводполя"
- •3. 10. Операции Расширяемость синтаксиса языка Пролог
- •Инфиксная операция ",", обладающая правой ассоциативностью
- •Инфиксная операция, не обладающая свойством ассоциативности
- •Объявление операций
- •3. 11. Преобразование процедурного алгоритма в программу на языке пролог Сравнение поиска с возвратом и рекурсии
- •Цикл "пока"
- •Рекурсия
- •Поиск с возвратом
- •Применение предиката "findall"
- •Библиографические заметки
- •Упражнения
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" циклически добавляется данной программой и удаляется ею, этот факт имеет некоторые черты глобальной переменной.
