
- •Введение
- •Принципы логического программирования
- •1.2. Математическая основа языка Пролог
- •1.2. Организация вычислительного процесса
- •1.2.2. Использование переменных
- •1.2.1. Синтаксис фактов и правил
- •1.3. Бэктрекинг
- •2. Основные элементы языка
- •2.1. Имена
- •2.2. Типы данных
- •2.3. Константы и переменные
- •2.4. Программные секции Пролога
- •2.4.1. Секция Domains
- •2.4.2. Секция Predicates
- •2.4.3. Секция Database
- •2.4.4. Секция Clauses
- •2.4.5. Секция Goal
- •3. Язык Пролог в задачах и примерах
- •3.1. Программирование с помощью фактов и правил
- •3.1.2. Первая формулировка задачи поиска в пространстве состояний 51 ад
- •3.1.3. Реализация на Прологе простой вопросно-ответной системы
- •It_is(“отказать в приеме на работу”):- not(this(“есть диплом”)).
- •It_is(“должность научного сотрудника”):- this(“есть диплом”),
- •3.2. Рекурсии
- •Упражнения
- •3.3. Программирование циклических процессов
- •3.4. Работа со списками
- •3.4.1. Описание списков в программе
- •3.4.2. Добавление элемента в список
- •3.4.3. Удаление элемента
- •3.4.4. Принадлежность элемента списку
- •3.4.5. Сцепление (конкатенация) списков
- •3.4.6. Удаление из списка повторяющихся элементов
- •3.4.7. Вычисление суммы элементов списка
- •3.4.8. Обращение списка
- •3.4.9. Нахождение максимального элемента списка
- •3.4.10. Перестановки
- •3.4.11. Примеры использования списков
- •Упражнения
- •3.5. Виды рекурсии
- •3.6. Поиск в пространстве состояний
- •Vshir ( [ [ V | Way ] | _ ], [ V | Way ] ) :- % Голова списка – полученное решение
- •Vshir ( Ways, Resh ). % Продолжение поиска в случае тупикового пути
- •Упражнения
- •3.6. Использование структур
- •3.6.1. Объявление структур
- •База данных с использованием структур.
- •Vife(X) :– family( _ , X , _ ). % X – жена
- •3.6.4. Планирование воздушного путешествия (143 Бр)
- •3.6.5. Реализация Планировщика в терминах структур
- •3.6.6. Задача «Зебра»
- •Упражнения
- •3.7. Динамическая база данных
- •3.7.1. Использование стандартных предикатов динамической базы данных
- •Упражнения
- •3.8. Средства управления
- •3.9. Представление множеств двоичными деревьями
- •3.9. Программы классификации
- •3.9.1. Программа классификации с обратной цепочкой рассуждений
- •Xpositive( X, y ), !. % в базе данных
- •Xnegative( X, y ), !, fail. % Отрицательный ответ обнаружен в базе данных
- •Xpositive("имеет","перья").
- •3.9.2. Программы классификации с прямой цепочкой рассуждений.
- •It_is( X ) :- write( X, “?”), % Механизм диалога
- •3.9. Обработка текстов
- •Verb( string ) % Глагол
- •Упражнения
- •4. Стандартные предикаты
- •4.1. Ввод/вывод
- •4.2. Управление экраном и оконная система
- •4.3. Обработка строк
- •4.4. Преобразование типов
- •4.5. Работа с базой данных
- •4.6. Управляющие предикаты
- •4.7. Прочие стандартные предикаты
- •4.8. Арифметические и логические предикаты
- •Приложение Приложение 1. Примерные варианты лабораторных заданий
- •1. Родословное дерево
- •2. Вопросно-ответная система
- •3. Работа со списками
- •4. Поиск пути на графе.
- •5. Разработайте прототип классификационной экспертной системы
- •6. Построение синтаксического анализатора
- •Рекомендуемая литература
3.4.1. Описание списков в программе
Для указания Прологу того, что мы предполагаем использовать списки в программе, используется второй формат указания Domains:
mylist = elevent* ,
где mylist – объявление списка элементов, element – один из стандартных типов Турбо-Пролога, либо ранее описанный пользователем в Domains; “*” – обязательный элемент синтаксиса, обозначает список. Например, домен
namlist = integer*
объявляет список целых чисел. Таким образом, шапка программы для работы со списком:
Domains
namlist = integer *
Predicates
add( integer, namlist, namlist ) % Объявляем предикат добавления элемента в список
Clauses
/* Далее следуют клозы предиката add */
Рассмотрим некоторые предикаты для обработки списков.
3.4.2. Добавление элемента в список
Требуется запрограммировать предикат add (добавить). Он должен иметь три аргумента: добавляемый элемент X, исходный список L и L1 - результирующий список:
add ( X, L, L1).
Поскольку речь не идет о том, что элемент требуется поставить на некоторое определенное место, то наиболее простой способ добавить элемент в список – это вставить его в самое начало так, чтобы он стал новой головой. В этом случае предикат имеет самый простой вид, можно обойтись без рекурсии. Если X – это новый элемент, который добавляют в список L, то предикат добавления элемента в список запишется следующим образом:
add( X, L, [ X | L ] ).
Отношение add можно использовать в обратном направлении для того, чтобы удалить голову списка. Заметим, что особенность работы предикатов Пролога «в обе стороны» является широко эксплуатируемой и представляет приятную неожиданность в декларативном программировании. Как же Пролог узнает, что делать со списком: отсекать ему голову или, наоборот, добавлять? На это вопрос легко ответить, если вспомнить основной вычислительный механизм Пролога: сопоставление и унификация. Если, например, встретится цель
Goal add( 5, [ 3, 6, 1], L ),
в которой третий аргумент не конкретизирован, то согласно реализации предиката add будет выдан ответ:
L= [ 5, 3, 6, 1 ].
Если же будет задана цель, в которой первый и третий аргументы конкретизированы, а второй – нет, Пролог конкретизирует его опять таки согласно алгоритму добавления, но с точностью наоборот:
Goal add( 5, L, [ 5, 3, 6, 1 ] ).
L = [ 3, 6, 1 ].
Если первый аргумент нас не интересует, вместо него можно указать анонимную переменную, тогда цель будет выглядеть еще проще (с тем же результатом):
Goal add( _ , L, [ 5, 3, 6, 1 ] ).
3.4.3. Удаление элемента
Удаление элемента X из списка L можно определить в виде отношения
away( X, L, L1 ),
где L1 – это список L, из которого удален элемент X.
Поскольку заранее не известно, где находится в списке удаляемый элемент, без рекурсивного перебора списка не обойтись.
Определим отношение away с использованием рекурсии следующим образом: если X является головой списка, тогда результатом удаления будет хвост этого списка T (первое правило, без рекурсии). Иначе, если X не совпадает с головой списка, т.е. находится в хвосте списка, тогда его нужно оттуда удалить:
away( X, [ X | T ], T ).
away( X, [ Y | T], [ Y | T1 ] ):– away( X, T, T1 ).
Если в списке встречается несколько вхождений элемента X, то away сможет исключить их все при помощи возвратов. Вычисление по каждой альтернативе будет удалять лишь одно вхождение X, оставляя остальные в неприкосновенности. Например:
goal away ( 5, L, [ 5, 3, 5, 6, 1, 5 ],).
L = [ 3, 5, 6, 1, 5 ].
L = [ 5, 3, 6, 1, 5 ].
L = [ 5, 3, 5, 6, 1 ].
При попытке исключить элемент, не содержащийся в списке, отношение away неудачу.
Отношение away можно использовать в обратном порядке для того, чтобы добавлять элементы в список, вставляя их в произвольные места. Операция по внесению X в произвольное место некоторого списка Spisok, дающее в результате больший список Bolsh_spis, может быть определена предикатом add2:
add2( X, Spisok, Bolsh_spis ):-
away ( X, Bolsh_spis, Spisok).