
- •Глава 1. Введение в пролог
- •1. Декларативные и процедурные языки программирования
- •2. Пролог и логика предикатов. Внешние цели
- •3. Управление программой. Подцели. Механизм сопоставления
- •4. Внутренние подпрограммы унификации
- •Глава 2. Внутренние цели. Механизм возврата
- •1. Структура пролог-программы
- •2. Использование внутренних целей
- •3. Встроенный предикат fail
- •4. Сокращенные варианты внутренних запросов
- •5. Использование в запросах анонимных переменных
- •6. Механизм возврата
- •Глава 3. Типы данных и арифметика Turbo Prolog
- •1. Стандартные типы данных
- •2. Структуры, простые и составные
- •3. Структурные диаграммы
- •4. Использование в запросах анонимных переменных
- •5. Использование альтернативных доменов
- •6. Арифметика в Turbo Prolog
- •Глава 4. Предикат отсечения (!). Программирование альтернатив. Правила повтора
- •1. Повторения и возвраты
- •2. Отсечение (!)
- •3. Программирование альтернатив
- •4. Правило повтора
- •Глава 5. Методы организации рекурсии
- •1. Простая рекурсия
- •2. Метод обобщенного правила рекурсии
- •3. Граничное условие рекурсии. Нисходящая и восходящая рекурсии
- •4. Программа о подсчете числа точек
- •Глава 6. Списки
- •1. Основные понятия
- •2. Списки и турбо-пролог
- •3. Атрибуты списка
- •4. Внутреннее представление списков
- •5. Применение списков в программе
- •6. Метод разделения списка на голову и хвост
- •7. Поиск элемента в списке
- •8. Присоединение списка
- •9. Добавление и удаление элемента
- •10. Подсписок
- •11. Перестановки списка
- •Глава 7. Сортировка списков
- •1. Разделение списка на два
- •2. Сортировка списков методом вставки
- •3. Быстрая сортировка
- •4. Быстрая сортировка_1
- •5. Компоновка данных в список
- •Глава 8. Программирование алгоритмов с возвратом. Представление графов в turbo prolog
- •1. Задача о весах
- •2. Представление графов в turbo prolog
- •3. Поиск пути на неориентированном графе
- •4. Поиск гамильтоновых циклов
- •5. Поиск пути минимальной стоимости
- •Глава 9. Динамическая база данных
- •1. Турбо-пролог и реляционные базы данных
- •2. Описание предикатов динамических бд
- •3. Встроенные предикаты asserta, assertz, retract, retractall, save, consult
- •4. Создание динамической базы данных
- •5. Обсуждение проекта базы данных
- •6. Создание базы данных
- •7. Написание программных модулей
- •Глава 10. Глобальные переменные в turbo prolog
- •1. Модификация базы данных
- •2. Накопление результатов с помощью вынуждаемого возврата
- •3. Подсчет членов парторганизации
- •4. Поиск пути минимальной стоимости от a до z
- •Библиографический список
- •Оглавление
6. Метод разделения списка на голову и хвост
В программе 6.1 «Списки» для получения доступа к элементам списков были использованы внешние целевые утверждения. Задание цели в виде animals(All) обеспечивало присваивание переменной All всего списка в целом. Напротив, цель animals([_,_,B,_]) позволила извлечь из списка лишь один элемент. В этом случае, однако, требовалось точное знание числа элементов списка.
Турбо-Пролог, однако, позволяет отделять от списка первый элемент и обрабатывать его отдельно. Данный метод работает вне зависимости от длины списка, до тех пор, пока список не будет исчерпан. Этот метод доступа к голове списка называется
МЕТОДОМ РАЗДЕЛЕНИЯ СПИСКА НА ГОЛОВУ И ХВОСТ.
Операция деления списка на голову и хвост обозначается при помощи вертикальной черты (|):
[Head|Tail]
Head здесь является переменной для обозначения головы списка, переменная Tail обозначает хвост списка. (Для имен головы и хвоста списка пригодны любые допустимые Турбо-Прологом имена.)
Программа 6.2 демонстрирует использования метода разделения списка. Два списка описаны в ней: список целых чисел (имя домена — integer*) и список символических имен (домен animals_list). Правило print_list применяется для доступа к элементам обоих списков.
/* Программа 6.2 «Голова-хвост». Назначение: */
/* разделение списка на голову и хвост */
domains
animals_list = symbol *
predicates
print_list(integer *)
print_list(animal_list)
clauses
print_list([]).
print_list([Head|Tail]) :-
write(Head),nl,
print_list(Tail).
/* Конец программы */
Программа 6.2 использует правило print_list для доступа к элементам списков. Так как предикат print_list определен для объектов обоих доменов, то это правило используется для работы как со списком number_list, так и списком animal_list.
Когда правило пытается удовлетворить внешнюю цель
print_list([4,-9,5,3])
то первый вариант правила, print_list[], дает неуспех, так как его объект является пустым списком. Напротив, введенный список соответствует объекту второго варианта предиката print_list([Head|Tail]). Переменной Head, следовательно, присваивается значение первого элемента в списке, 4, в то время как переменной Tail ставится в соответствие оставшаяся часть списка, [-9,5,3].
Теперь, когда выделен первый элемент списка, с ним можно обращаться так же, как и с любым простым объектом:
write(Head),nl
Так как хвост списка есть список сам по себе, значение переменной Tail может быть использовано в качестве объекта рекурсивного вызова:
print_list(Tail)
Процесс повторяется до тех пор, пока переменная Head не примет значение 3, а переменная Tail присвоится пустой список. Теперь при рекурсивном вызове print_list(Tail) значение Tail соответствует объекту правила
print_list([])
Этот вариант является граничным условием рекурсии, и цель считается удовлетворенной.
Рекурсивные правила для работы со списками просты, но вместе с тем и очень важны, ввиду их применимости в большинстве программ.
7. Поиск элемента в списке
Поиск элемента в списке является очень распространенной операцией. Поиск представляет собой просмотр списка на предмет выявления соответствия между элементом данных (объектом поиска) и элементом просматриваемого списка. Если такое соответствие найдено, то поиск заканчивается успехом. В противном случае поиск заканчивается неуспехом. Для сопоставления объекта поиска с элементами просматриваемого списка необходим предикат, объектами которого и явлются эти объект поиска и список:
member(3, [1,2,3,4,5])
Первый из объектов утверждения, 3, есть объект поиска. Второй — это список [1,2,3,4,5].
Для выделения элемента из списка и сравнения его с объектом поиска можно применить метод разделения списка на голову и хвост. Стратегия поиска при этом будет состоять в рекурсивном выделении головы списка и сравнении ее с элементом поиска.
Так же, как в программе «Голова-хвост», при рекурсии хвостом каждый раз становится новый список, голова которого присваивается переменной, сравниваемой с объектом поиска.
member(Head,[Head|_])
Этот вариант правила соответствует случаю, когда объект поиска совпадает с головой списка. Отметим, что хвост списка при этом присваивается анонимной переменной. Если объект поиска и голова списка действительно соответствуют друг другу, то результатом сравнения явится успех.
В случае неуспеха происходит возврат и поиск другого утверждения, с которыми можно снова попытаться найти соответствие. Для этого случая необходимо предусмотреть правило, которое выделило бы из списка следующий по порядку элемент и сделало бы его доступным для сравнения. Поскольку следующий за головой текущего списка элемент является головой текущего хвоста, мы можем представить хвост как новый список, голову которого можно сравнить с объектом поиска:
member(Head, [_,Tail]) :-
member(Head, Tail).
При этом уже проверенный первый элемент списка ставится в соответствие анонимной переменной. Попытка удовлетворить рекурсивное правило member(Head,Tail) заставляет Турбо-Пролог представить хвост текущего, как новый самостоятельный список.
Процесс повторяется до тех пор, пока это утверждение дает либо успех в случае установления соответствия на очередной рекурсии, либо неуспех в случае исчерпания списка.
Программа 6.3 демонстрирует реализацию операции поиска элемента в списке. Поскольку предикат member определен как для списков целых чисел, так и для списков символических имен, то в данной программе он работает со списками обоих типов.
/* Программа 6.3 «Элементы». Назначение: */
/* поиск нужного элемента в списке. */
domains
number_list = integer *
person_list = symbol *
predicates
member(number, number_list)
member(person, person_list)
goal
member(3,[1,2,3,4,5]), write(” нашли 3 ”),
member(john, [tom, bob, jerry, john],
write(” нашли Джона ”).
clauses
member(Head, [Head|_]).
member(Head, [_|Tail]) :-
member(Head, Tail).
/* Конец программы */
На экране появится True и печать двух сообщений.
Упражнение 6.2.
Задайте следующую внешнюю цель:
member(44,[11,44,11,33,44,44,55])
В скольких случаях будет достигнут успех?