Практикум по дисциплине Логическое и функциональное программирование
.pdf
ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ
Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования
Московский технический университет связи и информатики
Кафедра математической кибернетики и информационных технологий
Доткулова А.С.
ПРАКТИКУМ
по дисциплине
ЛОГИЧЕСКОЕ И ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ
Москва 2016
План УМД на 2016/17 уч.г.
ПРАКТИКУМ
по дисциплине
ЛОГИЧЕСКОЕ И ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ
Составитель А.С. Доткулова
Издание утверждено советом факультета ИТ. Протокол № 6
от 17.03.2016г.
2
Введение
При использовании ЭВМ для решения задач можно выделить два взаимосвязанных способа представления знания:
1)процедурное представление, т.е. определение алгоритма обработки данных;
2)декларативное представление, т.е. определение отдельных понятий, их состояния в конкретные моменты времени и связей между ними.
Традиционные алгоритмические языки (Паскаль, Си, Фортран) являются процедурными, поскольку их цель – описание алгоритма. Но они содержат и декларативные компоненты (описание переменных). В языке ПРОЛОГ (ПРОграммирование ЛОГическое), напротив, основной является декларативная компонента, так что он предназначен не только для обработки данных, но и для обработки фактов и декларативных правил. Факты представляют собой логические формулы. База знаний (БЗ) задается совокупностью таких формул. Логические методы обеспечивают получение новых фактов из фактов, представленных в БЗ. В процедурных языках программист должен описать для компьютера процесс решения задачи шаг за шагом. В противоположность этому в декларативном языке Prolog (ПРОЛОГ) программист описывает саму проблему и основные правила ее решения, оставляя за системой поиск конкретных действий, приводящих к решению. Поэтому Prolog справедливо считается языком более высокого уровня, чем Си или Паскаль. Внешним выражением этого факта является то, что текст программы на Prolog'е может быть в десять раз короче текста программы на Паскале, решающей ту же задачу (хотя, конечно, есть проблемы, неудобные для Prolog'а, но легко описываемые на Паскале или другом процедурном языке).
Prolog может использоваться при разработке экспертных систем, а также для следующих задач:
–доказательства теорем и вывода решений в задачах;
–создания пакетов символьной обработки при решении уравнений, дифференцировании, интегрировании и т.д.;
–разработки упрощенных версий систем ИИ;
–создания естественно-языковых интерфейсов для существующих программ;
–перевода текстов с одного языка на другой, в том числе – с одного языка программирования на другой.
Prolog появился в 1973 г., когда группа исследователей из Марсельского университета под руководством Алана Колмероэ создала программу для доказательства теорем, которая была реализована на языке Фортран.
3
Впоследствии этот продукт получил название Prolog. Первые годы Prolog не находил широкого практического применения.
Наибольшую популярность сначала получила система программирования Turbo Prolog – один из продуктов известной фирмы Borland, а затем в 1987 г. был выпущен и SWI Prolog. SWI Prolog работает на платформах Unix, Windows и Macintosh, поэтому именно на использование этой реализации языка рассчитаны все приводимые примеры и задачи.
Установить SWI Prolog можно с официального сайта http://www.swi-prolog.org/ .
Сам по себе Prolog – это логический язык программирования, основанный на предикатах. Предикат – это логическая функция одного или нескольких аргументов, принимающих значение true или false. Предикаты обозначаются P(x), P(x,y), P(x,y,z) и т.д. Например, предикат P(x), определенный на предметной области М, является истинным, если у объекта х есть свойство Р, ложным, если это свойство отсутствует. Предикат P(x,y) является истинным, если объекты х и у находятся в свойстве Р; ложным, если это свойство отсутствует. Отсюда вытекает, что многоместный
предикат |
P(x1, |
x2 , x3 ,..., xn) задает отношение между элементами |
||
x1, x2 , x3 |
,..., xn |
и интерпретируется |
как обозначение высказывания: |
|
“Элементы x1, x2 , x3 ,..., xn находятся между собой в отношении P”. |
||||
|
|
Синтаксис логики предикатов |
||
|
|
|
||
Предложения в разговорном языке |
Синтаксис логики предикатов |
|
||
|
|
|
|
|
Девочка красивая |
|
fun (girl) |
|
|
|
|
|
|
|
Роза красная |
|
red (rose) |
|
|
|
|
|
||
Ника любит розу, если роза красивая |
likes (nika, rose) if fun (rose) |
|
||
|
|
|
|
|
Предикаты, которые нельзя разбить на отдельные компоненты, называют атомарными. Сложные формулы строятся путем комбинирования атомарных предикатов логическими соединителями И, ИЛИ и НЕ. Одноместный предикат при подстановке в него значения переменной становится «нульместным», т.е. высказыванием, которое является истинным или ложным. При подстановке в n-местный предикат конкретного значения его местность становится равной n–1.
Имена объектов в ПРОЛОГЕ пишутся с маленькой буквы, а переменных – с большой.
Факты и правила
Программы на языке ПРОЛОГ состоят из двух типов фраз: фактов и правил, также называемых предложениями.
4
Факты — это отношения или свойства, о которых известно, что они имеют значение "истина".
Правила — это связанные отношения; они позволяют ПРОЛОГУ логически выводить одну порцию информации из другой. Правило принимает значение "истина", если доказано, что заданный набор условий является истинным.
Объекты
Программа на языке ПРОЛОГ обычно описывает некую действительность. Объекты описываемого мира представляются с помощью термов. Терм интуитивно означает объект. Существуют четыре вида термов: атомы, числа, переменные и составные термы. Атомы и числа иногда группируют вместе и называются простейшими термами.
Атом - это отдельный объект, считающийся элементарным. В Прологе атом представляется последовательностью букв нижнего и верхнего регистра, цифр и символа подчеркивания '_', начинающейся со строчной буквы. Кроме того, любой набор допустимых символов, заключенный в апострофы, также является атомом. Наконец, комбинации специальных символов + - * = < > : & также являются атомами (набор этих символов может отличаться в различных версиях Prolog’а).
Числа в ПРОЛОГЕ бывают целыми (Integer) и вещественными (Float). Переменными в ПРОЛОГЕ являются строки символов, цифр и символа подчеркивания, начинающиеся с заглавной буквы или символа подчеркивания: X, _4711, X_1_2, Результат, _x23, Объект2. Единственный символ подчеркивания ___ является особым случаем - анонимной переменной. Анонимная переменная применяется, когда ее значение не используется в программе. Возможное неоднократное употребление безымянной переменной в одном выражении применяется для того, чтобы подчеркнуть наличие переменных при отсутствии их специфической значимости.
Запросы
Однократно предоставив языку ПРОЛОГ несколько фактов, можно задавать вопросы, касающиеся отношений между ними. Это называется запросом системы языка ПРОЛОГ. Основываясь на известных, заданных ранее фактах и правилах, можно ответить на вопросы об этих отношениях, в
точности так же это может сделать ПРОЛОГ. |
|
Можно задать вопрос: |
|
Does Masha like Misha? (Маша любит Мишу?) |
likes (misha, masha). |
Получив такой запрос, ПРОЛОГ ответит: yes (да). |
|
What does Misha like? (Что любит Миша?) |
likes (misha, What). |
5 |
|
Важно отметить, что второй объект — What — начинается с большой буквы, тогда как первый объект — misha — нет. Это происходит потому, что misha — фиксированный, постоянный объект — известная величина, a What — переменная.
ПРОЛОГ всегда ищет ответ на запрос, начиная с первого факта, и перебирает все факты, пока они не закончатся. Чтобы просмотреть все факты, достаточно после ответа ставить ;.
Разделы Prolog-программы
Обычно программа на ПРОЛОГЕ состоит из четырех основных программных разделов. К ним относятся:
раздел clauses (предложений);
раздел predicates (предикатов);
раздел domains (доменов);
раздел goal (целей).
Раздел clauses — это сердце программы; именно в этот раздел записываются факты и правила, которыми будет оперировать ПРОЛОГ, пытаясь разрешить цель программы.
Раздел predicates — это тот, в котором объявляются предикаты и домены (типы) их аргументов.
Раздел domains служит для объявления своих доменов (типов данных).
Раздел goal — это тот, в который вы помещаете цель программы.
Списки
В ПРОЛОГЕ список — это объект, который содержит конечное число других объектов. Списки можно грубо сравнить с массивами в других языках, но, в отличие от массивов, для списков нет необходимости заранее объявлять их размер. Каждая составляющая списка называется элементом. Чтобы оформить списочную структуру данных, надо отделить элементы списка запятыми и заключить их в квадратные скобки.
Составные списки — это списки, в которых используется более чем один тип элементов. Для работы со списками из разнотипных элементов нужны специальные декларации. Для создания списка, который мог бы хранить различные типы элементов, в ПРОЛОГЕ необходимо использовать функторы, потому что домен может содержать более одного типа дан-
ных в качестве аргументов для функторов.
Деревья как типы данных
Каждая ветвь дерева сама является деревом, поэтому структура рекурсивна (например, фамильное дерево).
6
Например, можно определить дерево следующим образом: domains
treetype = tree(string, treetype, treetype)
Эта декларация говорит о том, что дерево записывается как функтор tree, аргументами которого являются строка и два других дерева. Решение состоит в том, чтобы определить два типа деревьев – обычное и пустое. Это достигается тем, что дерево может иметь один из двух функторов tree с тремя аргументами или empty без аргументов.
Вот как дерево, представленное на рисунке, будет описано в Prologпрограмме:
tree("Cathy",
tree("Michael", tree("Charles", empty, empty), tree("Hazel", empty, empty)), tree ("Melody",
tree("Jim", empty, empty), tree("Eleanor", empty, empty))) .
Одной из наиболее часто осуществляемых операций с деревом является исследование всех узлов и обработка их некоторым образом, либо поиск некоторого значения, либо сбор всех значений. Эти процедуры известны как обход дерева. Основной алгоритм для этого следующий.
1.Если дерево пусто, то ничего не делать.
2.Иначе, обработать текущее значение, затем перейти на левое поддерево, затем перейти на правое поддерево.
Как и само дерево, алгоритм является рекурсивным: он обрабатывает левое и правое поддеревья так же, как и исходное дерево.
Один из способов создания дерева – это вложенная структура из функторов и аргументов, как в предыдущем примере. Однако в общем случае ПРОЛОГ создает дерево путем вычисления. На каждом шаге пустое поддерево заменяется непустым в процессе унификации (сопоставления по аргументам).
Создание дерева из одного узла путем присвоения обычных данных тривиально:
create_tree(N, tree(N, empty, empty)).
7
Здесь говорится: "Если N – данное, то tree(N, empty, empty) – это дерево из одного узла, содержащее его".
Построение структуры дерева так же просто. Следующей процедуре нужны три дерева в качестве аргументов. Она вставляет первое дерево в качестве левого поддерева во второе дерево, и результат этого присваивает третьему дереву:
insert_left(X, tree(А, _, В), tree(А, X, В)).
Заметьте, что в этом предложении нет тела, т.е. нет четких шагов при его выполнении. Все, что должен сделать компьютер, – это соединить аргументы друг с другом в правильном порядке, – и работа закончена.
В деревьях имеется возможность хранить данные так, что их можно быстро отыскать. Дерево, построенное для такой цели, называется поисковым деревом. С точки зрения пользователя, сама структура дерева не несет информации, дерево – это более быстрая альтернатива списку или массиву.
Вспомним, что при обходе обычного дерева вы рассматриваете текущий узел, а затем оба его поддерева. Чтобы найти определенное значение, вы должны рассмотреть каждый узел всего дерева.
Бинарное поисковое дерево строится таким образом, что, глядя на любой узел, можно предсказать, в каком из его узлов находится заданное значение. Это делается заданием отношения порядка между значениями, таким как алфавитный или пронумерованный порядок. Значения в левом поддереве предшествуют значению в текущем узле, а в правом – следуют после значения в текущем узле. Заметьте, что те же имена, установленные в ином порядке, дадут другое дерево, и хотя в дереве десять имен, любое из них можно найти максимум за пять шагов.
Чтобы построить новое дерево, нужно начать с пустого дерева и добавлять к нему значения одно за другим. Процедура добавления значения такая же, как при поиске: необходимо просто найти место, где это значение должно находиться, и вставить его туда. Этот алгоритм заключается в следующем:
1.Если текущий узел есть пустое дерево, то вставить в него значение.
2.Иначе, сравнить значение, которое необходимо вставить, со значением в текущем узле. Вставить значение в левое или правое поддерево, в зависимости от результата сравнения.
Список лабораторных работ
1.Работа с отношениями на языке SWI Prolog.
2.Программирование структур данных на языке ПРОЛОГ.
3.Граф. Связность. Длина пути.
4.Трансформация формул.
8
Лабораторная работа № 1
Работа с отношениями на языке SWI Prolog
Порядок выполнения работы
1.Составить базовые отношения родитель, женщина, мужчина, супруги.
2.Определить в виде правил и представить на языке ПРОЛОГ отношения родства.
3.Набрать в текстовом редакторе системы ПРОЛОГ текст программы.
4.Задать следующие вопросы:
4.1) "Находится ли субъект а в отношении R с субъектом b ?"
4.1.1) "Является ли субъект a родственником типа R для субъекта b ?" [вопрос типа R(a,b)].
4.1.2) "Является ли субъект b родственником типа R для субъекта a ?" [вопрос типа R(b,a)].
4.2) "Кто находится в отношении R с субъектом a ?"
4.2.1) "Найти всех субъектов, для которых a является родственником типа R" [вопрос типа R(a,X)].
4.2.2) "Найти всех субъектов, которые являются родственниками типа R для субъекта b" [вопрос типа R(Y,b)].
4.3) "Найти все пары субъектов, находящихся в отношении R" [вопрос типа R(X,Y)].
5.Проверить вручную, соответствуют ли полученные ответы ожидаемому результату.
А. БЛИЗКИЕ КРОВНЫЕ РОДСТВЕННИКИ
1. |
отец |
8. внучка |
|
|
|
|
|
2. |
мать |
9. дедушка |
|
|
|
|
|
3. |
сын |
10. |
бабушка |
|
|
|
|
4. |
дочь |
11. тетя |
|
|
|
|
|
5. |
брат |
12. |
дядя |
|
|
|
|
6. |
сестра |
13. |
племянник |
|
|
|
|
7. |
внук |
14. |
племянница |
|
|
|
|
|
Б. НЕБЛИЗКИЕ КРОВНЫЕ РОДСТВЕННИКИ |
||
|
|
|
|
1. |
Правнук |
16. |
(N)-юродная тетя |
|
|
|
|
2. |
Правнучка |
17. |
двоюродный племянник |
|
|
|
|
3. |
(N)-правнук |
18. |
двоюродная племянница |
|
|
|
|
4. |
(N)-правнучка |
19. |
троюродный племянник |
|
|
|
|
5. |
двоюродный брат |
20. |
троюродная племянница |
|
|
|
|
|
|
|
9 |
6. |
двоюродная сестра |
21. |
(N)-юродный племянник |
|
|
|
|
|
|
7. |
троюродный брат |
22. |
(N)-юродная племянница |
|
|
|
|
|
|
8. |
троюродная сестра |
23. |
отпрыск (любого уровня) |
|
|
|
|
|
|
9. |
(N)-юродный брат |
24. |
(N)-юродный брат (любого уровня) |
|
|
|
|
|
|
10. |
(N)-юродная сестра |
25. |
(N)-юродная сестра (любого уровня) |
|
|
|
|
|
|
11. |
двоюродный дядя |
26. |
(N)-юродный дядя (любого уровня) |
|
|
|
|
|
|
12. |
двоюродная тетя |
27. |
(N)-юродная тетя (любого уровня) |
|
|
|
|
|
|
13. |
троюродный дядя |
28. |
(N)-юродный племянник |
|
|
|
|
|
|
14. |
троюродная тетя |
29. |
(N)-юродная племянница |
|
|
|
|
|
|
15. |
(N)-юродный дядя |
|
|
|
|
|
|
|
|
С. РОДСТВЕННИКИ ПО ЗАКОНУ
1. |
муж |
7. |
деверь [брат мужа] |
|
|
|
|
2. |
жена |
8. |
свояченица |
|
|
|
|
3. |
тесть |
9. |
свояк [муж свояченицы] |
|
|
|
|
4. |
теща |
10. зять |
|
|
|
|
|
5. |
свекор |
11. невестка |
|
|
|
|
|
6. |
свекровь |
12. золовка |
|
|
|
|
|
Варианты лабораторных заданий
№ |
Шифры родственных отношений |
||
|
|
|
|
1. |
А1, А11, Б7, Б17, Б24, С1 |
2. |
А13, Б3, Б10, Б21, С9 |
|
|
|
|
3. |
А2, А12, Б8, Б18, Б25, С2 |
4. |
А14, Б1, Б11, Б23, С8 |
|
|
|
|
5. |
А3, А13, Б9, Б19, Б23, С10 |
6. |
А5, Б2, Б12, Б17, С2 |
|
|
|
|
7. |
А4, А14, Б10, Б20, С9, С11 |
8. |
А3, Б5, Б14, Б21, С12 |
|
|
|
|
9. |
А5, Б1, Б11, Б21, С8, С12 |
10. |
А7, Б4, Б22, Б28, С6 |
|
|
|
|
11. |
А6, Б2, Б12, Б22, С7 |
12. |
А9, Б5, Б9, Б19, С7 |
|
|
|
|
13. |
А7, Б3, Б13, Б26, С6 |
14. |
А11, Б10, Б15, Б17, С3 |
|
|
|
|
15. |
А8, Б4, Б14, Б27, С5 |
16. |
А13, Б14, Б23, С6, С10 |
|
|
|
|
17. |
А9, Б5, Б15, Б28, С4 |
18. |
А1, А13, Б6, Б10, Б21, С2, С7 |
|
|
|
|
19. |
А10, Б6, Б16, Б29, С3 |
20. |
А5, Б17, Б23, А9, С12, С14 |
|
|
|
|
21. |
А11, Б8, Б13, Б22, С5 |
22. |
А8, А10, А12, Б7, Б10, Б12, Б20, |
|
|
|
С1, С7 |
|
|
|
|
23. |
А12, Б6, Б15, Б27, С1 |
24. |
А3, А5, А11, Б1, Б5, Б13, Б20, |
|
|
|
С3, С9 |
|
|
|
|
10
