
- •Введение
- •Принципы логического программирования
- •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. Построение синтаксического анализатора
- •Рекомендуемая литература
2.4.3. Секция Database
Объявление динамической базы данных. Синтаксис объявления такой же, как и в секции Predicates. Объявленные здесь предикаты не должны объявляться в секции Predicates, но дизъюнкты этих предикатов могут присутствовать в секции Clauses.
2.4.4. Секция Clauses
Здесь записываются все дизъюнкты всех предикатов. Другое название дизъюнктов – статьи, предложения или клозы. Это факты и правила, соответствующие каждому из объявленных предикатов. Примеры записи дизъюнктов-фактов:
child( “Иван” ).
mother( “Анна”, “Иван” ).
mother( “Анна”, “Петр” ).
Каждый из дизъюнктов одного предиката секции Clauses соответствует альтернативам этого предиката (т.е. реализация ИЛИ).
Главное синтаксическое требование при записи дизъюнктов – одноименные предикаты должны быть сгруппированы, т.е. записаны последовательно один за другим.
2.4.5. Секция Goal
Здесь указывается вопрос (цель), на который должен ответить Пролог. Записывается он как дизъюнкт без головы и знака ":–" , т.е. это конъюнкция подцелей. Например:
Goal
mother( “Анна”, X ), mother( X, Y ).
При запуске программы цель выполняется автоматически. Если все подцели в разделе Goal истинны, программа завершается успешно. Если же какая-то подцель из раздела Goal ложна, выполнение программы на этом заканчивается и она завершатся неуспешно.
Отметим, что запись цели явно в программе может отсутствовать. Это режим называется режимом диалога, и служит он в основном для отладки отдельных предикатов. В режиме диалога бэктрекинг включается автоматически, т.е. получает все решения для указанной цели.
3. Язык Пролог в задачах и примерах
3.1. Программирование с помощью фактов и правил
Рассмотрим следующую вводную Пролог-программу. На этом примере мы покажем, как создать, отредактировать и выполнить Пролог-программу в интегрированной среде PDC Prolog.
predicates
hello
clauses
hello :-
makewindow(1,7,7,"My first program",4,36,10,22), % Создание окна
nl, % Перевод строки
write(" Please type your name "), % Вывод текстовой строки
cursor(4, 5), % Позиционирование курсора
readnl( Name ), % Ввод строки в переменную Name
nl,
write(" Wellcome ", Name). % Вывод текстовой строки и переменной
goal
hello.
После исправления ошибок в программе выберите опцию RUN в меню PDC Prolog. Введите свое имя (например, Аня) в окне ввода и нажмите ENTER. Программа напечатает
Welcome Аня
и будет ждать нажатия клавиши пробела (SPACE BAR).
Работа программы всегда начинается с выполнения раздела Goal. Цель пытается удовлетворить предикат hello. Этот предикат не имеет аргументов, его описание есть в разделе Predicates. Имеется единственная статья Clauses для этого предиката. Поэтому для проверки истинности цели начинают последовательно выполняться предикаты, записанные в правой части предиката hello. Все эти предикаты являются встроенными (стандартными), поэтому в программе не требуется их описания.
Эту небольшую программу привели только для демонстрации запуска полностью готовой программы в среде PDC Prolog. Она похожа на процедурный стиль программирования и напоминает, скажем так, реализацию некоторой логической функции (поскольку предикат, как логическая функция, может принимать только одно из двух значений – истина или ложь). Данный пример приведен, чтобы искушенный в программировании пользователь смог провести аналогию между процедурным стилем программирования и возможностью его реализации на Прологе.
Второй пример сделаем уже в прологовском стиле. Пролог – это язык программирования, предназначенный для представления и использования знаний о некоторой предметной области. Более точно, предметная область – это множество объектов вместе со знанием о них, выраженном в виде отношений. Эти отношения описывают свойства объектов и связи между ними. Описания представлены в виде фактов и правил. Это описание статическое, само по себе оно никакого процесса вычисления не задает. Чтобы программа заработала, необходимо указать цель.
Оформим факты и правила примера из раздела 1.2 в виде работающей Пролог-программы, добавив секцию описания предикатов Predicates и название секции их реализации Clauses:
Predicates
likes( string, string )
clever ( string )
Clauses
likes( ellen, reading ).
likes( john, football ).
likes( tom, baseball ).
likes( eric, reading ).
likes( mark, tennis ).
clever ( X ) :- likes ( X, reading ).
Эта программа может выполняться в интерактивном режиме (режиме диалога), поскольку цель явно не записана в программе (отсутствует секция Goal).
Запустите среду PDC PROLOG и наберите в окне редактора текст этой программы. После исправления синтаксических ошибок в программе в процессе компиляции выберите опцию RUN в меню Пролога. В этой программе цель не указана (которая, вообще говоря, в отладочном режиме не обязательна), поэтому, когда система в отдельном диалоговом окне после приглашения «Goal:» запросит цель, сразу после двоеточия введите запрос:
Goal: likes( john, football ).
Пролог ответит
Yes ,
поскольку такой факт имеется. Каким образом отыскивается этот факт в базе данных – это уже забота самой Пролог-системы. Программист только сообщает системе то, что ему известно и задает вопросы.
Заметим, что хотя программа и в таком виде будет работать, более грамотным является запись программы с указанием областей определения переменных:
domains
name, hobby = string
Predicates
likes( name, hobby )
clever ( name )
Clauses
likes( ellen, tennis ).
likes( john, football ).
likes( tom, baseball ).
likes( eric, swimming ).
likes( mark, tennis ).
clever ( X ) :- likes ( X, reading ).
2. Построение базы данных. На Прологе чрезвычайно просто организовать реляционную базу данных в виде набора некоторых фактов и реализовать всевозможные запросы в виде правил. Пусть база данных содержит следующие сведения об автомобилях: марка машины, год выпуска, цвет, цена:
Predicates
car( string, integer, string, integer )
Clauses
car( volvo, 1990, red, 1800 ).
car( toyota, 1988, black, 2000 ).
car( ford, 1994, white, 3000 ).
Наберите эту программу согласно правилам оформления Пролог-программы и исполните команду RUN. Когда система запросит цель, введите запрос:
Goal: car( toyota, _ , _ , _ ).
Пролог ответит
1 solution
Yes
в диалоговом окне и будет ждать, когда вы введете другую цель.
В этом запросе использовались анонимные переменные, обозначенные символом подчеркивания. Анонимная переменная используется для обозначения аргументов, значения которых в данный момент могут быть произвольны. Если анонимная переменная встречается в запросе, то ее значение не выводится при ответе системы на этот запрос. Анонимная переменная может также использоваться в записи предикатов, если в предложении она встречается один раз. Примеры записи таких предикатов встретим дальше.
Если зададите в качестве цели
Goal: car( mersedes, 1990, _ , _ ) ,
система ответит
No solution,
поскольку наша база данных не содержит такого утверждения.
Теперь предположим, что мы хотим узнать все марки машин, имеющиеся в базе данных, т.е. задать вопрос: "Каковы те объекты Х, которые являются марками машины?". Тогда наш вопрос запишется так:
Goal: car( X, _ , _ , _ ).
Пролог ответит:
X = “volvo”
X = "toyota"
X = "ford"
3 solutions.
Можно также задавать вопросы более общего характера. К примеру, если требуется найти все машины, выпущенные до 1992 года, то следует задать запрос, который представляет конъюнкцию целей:
Goal car( X, Y, _ , _ ), Y < 1992.
X = “volvo” Y = 1990
X = "toyota" Y = 1988
2 solutions.
Подобная база данных имеет вид целостных информационных объектов. Более гибким является представление базы данных в виде бинарных отношений, в которых один из атрибутов должен выступать в роли ключа, объединяющего все остальные свойства. Пусть в нашем примере это будет марка машины:
year( volvo, 1990 ).
year( toyota, 1988 ).
year( ford, 1994 ).
color( volvo, red ).
color( toyota, black ).
color( ford, white ).
cost( volvo, 2000 ).
cost( toyota, 2500 ).
cosr( ford, 3000 ).
В случае необходимости атрибуты можно собрать в единое целое с использованием переменных при помощи правила:
car( M, Y, C, P ) :– year( M, Y ), color( M, C ), cost( M, P ).
Правило в данном случае является обобщением всех фактов.
Определим теперь запрос, выполняющий поиск старых автомобилей, в виде правила «старый автомобиль»:
old_car( M, Y ) :- car( M , Y, _ , _ ), Y < 1992.
Это правило добавим к основной программе, не забыв включить го описание в раздел Predicates, а обращаться к нему можно, задав цель:
Goal: old_car( X, Y ).
Даже уже в этой простой программе мы вплотную подошли к одному из определений базы знаний. База знаний – это набор фактов и правил. Пока у нас было описание машин в виде фактов, это была база данных. Как только программу дополнили правилами, т.е. указали способ использования данных, мы перешли уже к базе знаний.
4. Любитель проводить аналогию между различными языками программирования уже догадался, что Пролог-программа ведет себя так, как если бы каждая строка программы начиналась с ключевого слова IF (ЕСЛИ). Поэтому разветвляющиеся алгоритмы реализуются в Прологе достаточно прямолинейно.
Программа решение квадратного уравнения – типичный пример реализации на Прологе разветвляющегося алгоритма. В комментариях приведена интерпретация предикатов.
predicates
solve( 0real, real, real ) % Запуск программы
reply( real, real, real ) % Ветви вычислений
clauses
solve( A, B, C ) :–
D = B * B – 4 * A * C, % Вычисление дискриминанта
reply( A, B, D ), nl. % Здесь запускаются разветвления вычислительного процесса
reply( _ , _ , D) :– D < 0,
write( "No solution" ).
reply( A, B, D ) :– D = 0,
X = - B / ( 2 * A ), write( "x1,2=", X ).
reply( A, B, D ) :– D > 0,
SqrtD = sqrt( D ),
X1 = ( - B + SqrtD) / ( 2 * A ),
X2 = ( - B – SqrtD) / ( 2 * A ),
write( "x1 = ", X1," and x2 = ", X2 ).
Goal solve( 3, 4, 5 ).
На этом примере видно, что главный способ реализации разветвлений в Пролог-программе – обращение к некоторому предикату, который реализуется с проверкой заданных условий (в нашем случае – предикат reply).
В этом примере также видим использование анонимной переменой в первом предложении предиката reply:
reply( _ , _ , D) :– D < 0, write( "No solution" ).
Оно говорит, что если дискриминант D меньше нуля, неважно, какие значения имеют коэффициенты A и B.
Рассмотрим другой пример в качестве упражнения по автоматизации логического вывода. Имеется следующая задача из психологического практикума.
Бутси – коричневая кошка. Корни – черная кошка. Мак – рыжая кошка. Флэш, Ровер, Спот – собаки, Ровер – рыжая, а Спот – белая. Все животные, которыми владеют Том и Кейт, имеют родословные. Том владеет всеми черными и коричневыми животными, а Кейт владеет всеми собаками небелого цвета, которые не являются собственностью Тома. Алан владеет Мак, если Кейт не владеет Бутси и если Спот не имеет родословной. Флэш – пятнистая собака. Определить, какие животные не имеют хозяев. (Ответ: Спот).
Для интерпретации формул исчисления предикатов требуется задать области интерпретации предметных переменных, что делается в разделе Domains
Domains
animal_name, color, name = string
Описание предикатов включает имена предикатов и перечисление имен предметных переменных:
Predicates
cat( animal_name )
dog( animal_name )
color( animal_name, color )
have( name, animal_name )
rodosloмnaya(animal_name )
animal( animal_name)
Факты и правила следующие:
Clauses
/* Факты, перечисляющие клички и расцветки животных */
cat( butsi ). cat( korni ). cat( mac ).
dog( rover ). dog( fles ). dog( spot ).
color( butsi, brown ).
color( korni, black ).
color( mac, yellow ).
color( rover, yellow ).
color( spot, white ).
color( fles, black_and_white ).
/* Животное – это кошка или собака*/
animal( X ) :– cat( X ); dog( X ).
/* Животные, имеющие родословные */
rodoslovnaya( X ) :– animal( X ), have( tom, X ).
rodoslovnaya( X ) :– animal( X ), have( keit, X ).
/* Том владеет всеми черными или коричневыми животными */
have( tom, X ) :– color( X, black ); color( X, brown ).
/* Кейт владеет собаками */
have( keit, X ):–dog( X ),
not( color( X, white )),
not( have( tom, X )).
/* Алан владеет Мак */
have( alan, mac ) :– not( have( keit, butsi )),
not( rodoslovnaya( spot )).
/* Цель – найти животное Х, не имеющее владельца */
goal
animal( X ), not( have( _ , X )),write( X ).
Цель организует запрос к программе и выводит результат. Процесс доказательства включает в себя как поиск нужных подстановок, которые делают истинными утверждения, так и выполнение встроенных предикатов. Например, not – встроенный предикат отрицания, write( ) – вывод значения переменной.
Каждое предложение раздела Clauses представляет собой формулу вида:
D <– A & B & … & C.
Например, предложение
rodoslovnaya(X):–animal(X), have(tom,X).
представляет собой формулу
animal(X) & have(tom,X) –> rodoslovnaya(X),
причем порядок следования элементов конъюнкции определяет последовательность процесса доказательства по методу резолюций и может имеет важное значение.