
- •Предисловие
- •Глава 1 Введение в Пролог
- •Глава 2 Синтаксис и унификация
- •2.1. Синтаксис термы
- •Константы
- •Переменные
- •Область действия переменных
- •Сложные термы, или структуры
- •Синтаксис операторов
- •Синтаксис списков
- •Синтаксис строк
- •Утверждения
- •Запросы
- •Ввод программ
- •2.2 Унификация
- •Глава 3 Арифметические выражения
- •3.1. Введение
- •3.2. Арифметические выражения
- •3.3. Арифметические операторы
- •3.4. Вычисление арифметических выражений
- •3.5. Сравнение результатов арифметических выражений
- •Глава 4 Рекурсия
- •4.1. Стратегия «разделяй и властвуй»
- •Пример 4.1.1
- •Фаза разбиения
- •Фаза решения задачи
- •4.2. Восходящая стратегия
- •4.3. Рекурсия и эффективность
- •Пример 4.3.2
- •Глава 5 Структуры данных
- •5.1. Списки списковая форма записи
- •А на запрос
- •Некоторые стандартные целевые утверждения для обработки списков
- •5.2. Бинарные деревья представление бинарных деревьев
- •Бинарное дерево на рис.5.2.1 имеет левое поддерево
- •Представление множеств с помощью бинарных деревьев
- •Будем называть линейным представление такого вида, как на рис.5.2.3, и сбалансированным - такое, как на рис.5.2.2.
- •Левому поддереву соответствует отсортированный список
- •Глава 6 Операторы
- •6.1. Операторы и структуры синтаксис операторов
- •Свойства операторов
- •6.2. Позиция операторов
- •6.3. Приоритет операторов
- •6.4. Ассоциативность операторов
- •6.5. Спецификаторы
- •6.6. Операторы объявления
- •6.7. Пример: вычисление многочленов
- •6.8. Системные операторы
- •Глава 7 Механизм возврата и процедурная семантика
- •7.1. Механизм возврата
- •7.2. Пример: задача поиска пути в лабиринте
- •Целевое утверждение не удается согласовать с первым утверждением
- •Если мы введем
- •Если затем мы введем
- •7.3. Обработка фактов с помощью механизма возврата
- •7.4. Предикат repeat
- •7.5. Декларативная и процедурная семантики
- •Характеристики процедуры
- •7.6. Вопросы эффективности
- •Глава 8 Отсечение
- •8.1. Почему используют отсечение?
- •8.2. Использование отсечения
- •Выдав запрос
- •Обратившись к системе с запросом
- •8.3. Ловушки отсечения
- •Глава 9 Встроенные предикаты
- •9.1. Встроенные предикаты
- •9.2. Обновление базы данных Пролога
- •Добавление и удаление утверждений
- •Считывание утверждений в базу данных
- •Предикаты сохранения и восстановления
- •9.3. Особенности ввода и вывода чтение символов
- •Запись символов
- •Считывание термов Предикаты
- •Запись термов
- •9.4. Обработка файлов
- •Редактирование программ на прологе
- •Распечатка предикатов
- •Глава 10 Модули. Пример разработки системы
- •10.1. Модули
- •Пример 10.1.1
- •10.2. Пример разработки системы: деревья решений
- •Описание дерева решений
- •Разработка системы
- •Целевое утверждение
- •Глава 11 Отладка
- •11.1. Трассировка
- •Включение и выключение механизма трассировки
- •Необязательные параметры трассировки
- •Предикаты трассировки
- •Режимы трассировки
- •11.2. Установка контрольных точек
- •Возможные действия в контрольной точке
- •Включение и выключение режима контрольных точек
- •Необязательные параметры режима контрольных точек
- •Предикаты для работы с контрольными точками
- •Режимы, связанные с контрольными точками
- •Основные отладочные предикаты
- •Статистическая информация
4.3. Рекурсия и эффективность
Определение рекурсии можно непосредственно выразить утверждениями Пролога.
Однако следующий пример показывает, что для создания более эффективной программы иногда лучше воспользоваться особенностями задачи.
Пример 4.3.1
Рассмотрим методы вычисления Х^n с помощью одного только умножения. Здесь n - натуральное число (натуральным числом является положительное целое число или нуль).
Очевидный способ - умножить Х на себя n раз. Получим следующие утверждения:
степень(Х,О,1). /* Х^О равно 1
степень(Х,N,R) :-
М is N - 1,
степень(Х,М,0),
R is Q*X.
Тогда ответом на запрос
?- степень(5,8,S).
будет:
S = 390625
Этот результат получен с помощью 8 умножений (рис.4.3.1).
Но можно также воспользоваться свойствами:
Х^n = Z * Z, где Z = X^(n div 2), если n - четное, и
Х^n = Х^(n - 1) * X, если n - нечетное,
и получить следующие утверждения:
степень_быстро(Х,0,1).
степень_быстро(X,N,R) :-
0 is N mod 2,
М is N div 2,
степень_быстро(Х,М,Q),
R is Q*Q.
степень_быстро(X,N,R) :-
М is N - 1,
степень_быстро(Х,М,Q),
R is Q * X.
Во втором случае понадобится только 4 умножения (рис.4.3.2) при обработке запроса
?- степень_быстро(5,8,S).
и будет получен ответ
S = 390625
Заметим, что в третьем утверждении степень_быстро нам не нужно было проверять, является ли N нечетным. Мы предполагаем, что второе утверждение не сопоставляется с целью в том случае, если N mod 2 не равно 0, т.е. N не является четным. Следовательно, третье утверждение выбирается только тогда, когда N является нечетным (за исключением вопросов об альтернативных вариантах).
Рис.4.3.1. Трассировка запроса ?-степень(5,8,S)
Рис.4.3.2. Трассировка запроса степень_быстро(5,8,S)
Преимущество, которое имеет утверждение степень_быстроперед утверждениемстепень, увеличивается с ростомN. Так, согласование утверждениястепень(2,1024,Х)требует 1024 умножений, в то время как согласование утверждениястепень_быстро(2,1024,Х)требует всего 11 умножений. Необходимость дополнительной проверки четностиNи деленияNпополам приводит к тому, что практически только при большихNпроцедурастепень_быстропредпочтительнее, чем процедурастепень.
Пример 4.3.2
Ряд Фибоначчи
0,1,1,2,3,5,8,13,21,34,55,89,144,...
определяется условиями:
f0 = 0
f1 = 1
fn = f(n-l) + f(n-2)
Мы можем непосредственно использовать определение ряда:
фиб(0,0).
фиб(1,1).
фиб(N,X) :-
N1 is N - 1,
N2 is N - 2,
фиб(N1,Х1),
фиб(N2,Х2),
X is Xl + X2.
Для того чтобы согласовать фиб(N,X), потребуется 2*Y-1 рекурсивных обращений. Здесь N больше нуля, a Y является (N+1)-m числом Фибоначчи.
Так, например, для согласования запроса
?- фиб(10,Х).
при
Х=55
потребуется 177 (89*2-1) обращений.
Однако можно переформулировать задачу следующим образом: найти N-e и (N+1)-e числа Фибоначчи. Тогда мы получим:
/* Нулевое число Фибоначчи равно 0, а
/* предшествующее число не определено
фиб(0,_,0).
/* Первое число Фибоначчи равно 1, а
/* предшествующее равно 0
фиб(1,0,1).
/* N-e число Фибоначчи образуется при
/* сложении двух предшествующих чисел
/* Фибоначчи
фиб(N,F1,F2) :-
M is N- 1,
фиб(М,F0,F1),
F2 is F0 + Fl.
Чтобы найти 10-е число Фибоначчи, сделаем запрос:
?-фиб(10,F1,F2).
получим
F1 = 34
F2 = 55
другие решения (да/нет)? нет
Рис.4.3.3. Сравнение процедур фиб/2 и фиб/3
Для ответа на запрос было произведено 10 обращений. В общем для нахождения N-ro числа Фибоначчи (для N > 0) требуется N обращений. На рис.4.3.3 показано, как с ростом N увеличивается преимущество второго варианта процедуры (фиб/3) перед первым (фиб/2).