
- •Введение
- •Принципы логического программирования
- •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. Построение синтаксического анализатора
- •Рекомендуемая литература
Vife(X) :– family( _ , X , _ ). % X – жена
child(X) :– family( _ , _ , Children ), % X – ребенок
member( X , Children ).
exist(X) :– husband( X ); vife( X ); child( X ). % X – любой член семьи
Этими процедурами можно воспользоваться, например, в следующих запросах к базе данных:
1) найти имена и фамилии всех людей из базы данных:
Goal: exist( mem( Nam, Fam, _ , _ )).
2) Найти всех работающих жен:
Goal: vife(mem( Nam, Fam, _ ,worker( _ , _ ))).
Другой способ извлечения информации из структурированной базы данных – через так называемый предикат-селектор. Селектор позволяет извлечь Общий вид такого предиката-селектора:
селектор(Структурный объект, Выбранная компонента).
Селектор позволяет извлечь отдельные компоненты структуры. Например, если хотим извлечь величину дохода отдельного члена семьи, следует записать селектор:
dohod(mem( _ , _ , _ , worker( _ , D)), D ). % D – доход работающего
dohod(mem( _ , _ , _ , notwork), 0 ). % 0 – доход неработающего
Ясно, что селектор требует конкретизированный первый аргумент, поэтому селектор всегда используется в правой части правила или в цели, предварительно конкретизирующей структуру. Тогда, чтобы найти людей, чей доход меньше чем 8000, можно записать цель:
Goal: exist(Man), dohod(Man, D), D < 8000.
Видно, что при использовании селектора структурный объект (переменная Man) задан как единое целое. Пролог прекрасно справляется с «расщеплением» структуры и извлечением из нее отдельных единиц информации. Использование отношений-селекторов позволяет забыть о деталях представления информации и облегчает написание и последующую модификацию программ.
3.6.2. Задача "Ханойская башня". Имеется три стержня А,В,С. На стержне А лежит N дисков пирамидой, сужающейся кверху. Надо переместить диски со стержня А на стержень C, используя промежуточный стержень B и соблюдая законы:
1) диски можно перемещать с одного стержня на другой по одному;
2) нельзя класть больший диск на меньший;
3) при переносе дисков с одного стержня на другой можно использовать промежуточный третий стержень, на котором диски должны находиться тоже только в виде пирамиды.
Если в программе использовать рекурсивную процедуру, то программа становится удивительно маленькой: два рекурсивных вызова процедуры и оператор вывода результатов перемещения дисков.
Собственно "перемещению" диска соответствует оператор вывода, указывающий, с какого стержня на какой переместить диск. Основным рабочим предикатом является рекурсивный предикат move. Базовое правило этой рекурсии: если диск один, то переместить его с левого стержня на правый. Иначе, переместить N-1 диск с левого диска на средний промежуточный, переместить один диск с левого стержня на правый и затем переместить N-1 диск со среднего стержня на правый, используя левый стержень как промежуточный.
Domains
loc = right; middle; left
predicates
hanoi(integer)
move(integer, loc, loc, loc)
inform(loc, loc)
clauses
hanoi(N) :– move(N, left, middle, right). % Запуск программы
move(1, A, _, C) :– inform(A, C), !. % Переместить один диск
move(N, A, B, C) :– % Переместить N дисков
N1=N-1, move(N1, A, C, B),
inform(A, C), move(N1, B, A, C).
inform(Loc1, Loc2) :– write("\nMove a disk from ", Loc1, " to ", Loc2).
Goal hanoi(10). % Перемещаем 10 дисков
3.6.3. Задача о перевозке через реку волка, козы и капусты. Предполагается, что вместе с человеком в лодке помещается только один объект и что человеку приходится охранять козу от волка и капусту от козы. Типичная задача, описываемая пространством состояний. Пространство состояний - это граф, вершины которого соответствуют ситуациям, встречающимся в задаче, а решение задачи сводится к поиску пути в этом графе между заданной начальной ситуацией (стартовой вершиной) и некоторой конечной ситуацией, называемой целевой вершиной.
Пространство состояний можно указывать явно в виде набора двухаргументных отношений. Такое отношение истинно, если существует разрешенный ход. Однако этот принцип оказывается непрактичным и нереальным, когда пространство состояний достаточно велико. Поэтому отношение следования обычно определяется неявно при помощи правил вычисления вершин-преемников некоторой заданной вершины. Использование структур дает возможность программировать пространство состояний, взамен полного перебора их описаний.
Каждый из четырех объектов (фермер, волк, коза и капуста) может находиться в одном из двух состояний LOC: east или west, в зависимости от того, на каком берегу (восточном или западном) они находятся. Общее состояние всех субъектов описывается структурой STATE. Понятно, что если в начальный момент все объекты находятся в состоянии east, то программа должна перевести их в состояние west, избегая запрещенных состояний. К запрещенным состояниям относятся следующие: фермер в одном состоянии, например east, а волк и коза – в состоянии west, т.е. при отсутствии фермера волк съест козу; к другим запрещенным состоянием относятся присутствие вместе козы и капусты в отсутствие фермера. Последовательность допустимых переходов структур STATE даст искомое решение.
DOMAINS
LOC = east ; west
STATE = state(LOC,LOC,LOC,LOC)
PATH = STATE*
PREDICATES
go(STATE,STATE) % запуск программы
path(STATE,STATE,PATH,PATH) % основной рабочий предикат
move(STATE,STATE) % одна перевозка
opposite(LOC,LOC) % переход в противоположное состояние
unsafe(STATE) % запрещенное состояние
member(STATE,PATH) % проверка принадлежности
write_path(PATH) % оформление вывода
GOAL
makewindow(4, 7, 0, "", 0, 0, 24, 80),
go(state(east, east, east, east), state(west, west, west, west)),
write("solved press any key to continue"),
readchar( _ ),
exit.
CLAUSES
go(S,G) :- path( S, G, [ S ], L ),
nl, write("A solution is:"), nl,
write_path( L ),
fail.
go( _ , _ ).
path( S, G, L, L1 ) :- move( S, S1 ),
not( unsafe( S1 ) ),
not( member( S1, L ) ),
path( S1, G, [ S1 | L ], L1 ), !.
path( G, G, T, T ):- !. % Базовое правило, последний
% аргумент фиксирует результат
move(state( X, X, G, C ), state( Y, Y, G, C )):-opposite( X, Y ). % фермер + волк
move(state( X, W, X, C ), state( Y, W, Y, C )):-opposite( X, Y ). % фермер + коза
move(state( X, W, G, X), state( Y, W, G, Y )):-opposite( X, Y ) . % фермер + капуста
move(state( X, W, G, C ), state( Y, W, G, C )):-opposite( X, Y ). % только фермер
opposite( east, west ).
opposite( west, east ):- !.
unsafe( state( F, X, X, _ ) ):- opposite( F, X ). /* волк съест козу */
unsafe( state( F, _ , X, X ) ):- opposite( F, X ). /* коза съест капусту */
member( X , [ X | _ ] ).
member( X, [ _ | L ] ):-member( X, L ).