- •Лабораторная работа № 1. Малая экспертная система.
- •Теоретические сведения
- •Диагностические экспертные системы.
- •Пример применения байесовской стратегии оценки выводов
- •Контрольные задания:
- •Контрольные вопросы:
- •Лабораторная работа № 2. Знакомство с инструментальными средствами для создания экспертных систем.
- •Теоретические сведения
- •Контрольное задание:
- •Контрольное задание:
- •Контрольные вопросы:
- •Лабораторная работа № 4.
- •Теоретические сведения
- •Контрольное задание:
- •Основные стандартные домены
- •Основные стандартные предикаты:
- •Ключевые слова
- •Контрольные задания:
- •Контрольные вопросы:
- •Лабораторная работа №7. Предложения (факты и правила), цели на языке пролог.
- •Теоретические сведения Clauses (условия): Facts (факты) и Rules (правила)
- •О фактах
- •Контрольные вопросы:
- •Лабораторная работа №8. Переменные на языке программирования пролог.
- •Теоретические сведения
- •Анонимные переменные
- •Контрольное задание:
- •Принципы отката:
- •Контрольные задания:
- •Преимущества рекурсии
- •Оптимизация обратной рекурсии
- •Контрольные задания:
- •Объявление списков
- •Головы и хвосты
- •Обработка списков
- •Использование списков
- •Контрольное задание:
- •Контрольные вопросы:
- •Лабораторная работа №13. Секция фактов Лабораторная работа № 1.
- •Теоретические сведения
- •Объявление секций фактов
- •Модификация секции фактов
- •Добавление фактов в период исполнения программы
- •Загрузка фактов из файла в период исполнения программы
- •Удаление фактов в период исполнения программы
- •Удаление нескольких фактов сразу
- •Ключевые слова для объявления фактов
- •Описания Факты, объявленные с ключевым словом nondeterm
- •Факты, объявленные с ключевым словом determ
- •Факты, объявленные с ключевым словом single
- •Сохранение базы данных фактов во время выполнения программы
- •Контрольные задания:
- •Контрольные вопросы:
Головы и хвосты
Список - действительно рекурсивный составной объект. Состоит из двух частей: голова списка, которая является первым элементом, и хвостовой части, которая является списком, включающим все последующие элементы. Хвост списка - последний элемент списка. Например,
Головой из списка [a, b, c] является a, хвостовой частью - [b, c], хвостом - c.
Если Вы берете первый элемент от хвостовой части списка много раз, Вы в конечном счете перейдете к пустому списку. Пустой список не может быть разбит на голову и хвостовую часть.
Это означает, что, концептуально, списки имеют древовидную структуру точно так же как другие составные объекты. Древовидная структура [a, b, c, d]:
list
/ \
a list
/ \
b list
/ \
c list
/ \
d []
Обработка списков
Пролог обеспечивает способ создавать голову и хвостовую часть явного списка. Вместо того, чтобы отделять элементы запятыми, Вы можете отделить голову и хвостовую часть вертикальной чертой (|). Например,
[a, b, c] is equivalent to [a|[b, c]]
и, продолжая процесс,
[a|[b, c]] = [a|[b|[c]]] = [a|[b|[c|[]]]].
Использование списков
Поскольку список - действительно рекурсивная составная структура данных, Вы нуждаетесь в рекурсивных алгоритмах, чтобы обработать его. Самый основной способ обрабатывать список состоит в том, чтобы работать с каждым элементом списка, пока Вы не достигаете его конца.
Алгоритм этого вида обычно нуждается в двух предложениях. Один из них говорит, что сделать с обычным списком (тот, который может быть разделен на голову и хвостовую часть). Другой говорит, что сделать с пустым списком.
Составные списки
Составные списки - списки, которые содержат больше чем один тип элемента. Вы нуждаетесь в специальных объявлениях, чтобы обработать списки элементов множественного типа, потому что Visual Prolog требует, чтобы все элементы в списке принадлежали тому же самому домену. Способ создавать список в Прологе, который сохраняет эти различные типы элементов, состоит в том, чтобы использовать функторы, потому что домен может содержать больше чем один тип данных как параметры к функторам.
Рассмотрим пример:
База данных содержит факты вида: student(имя, курс). Создать проект, позволяющий сформировать список студентов 1-го курса.
Создайте новый проект:
PREDICATES
nondeterm student(symbol, integer)
spisok
CLAUSES
student(vova, 3).
student(lena, 1).
student(dima, 1).
student(ira, 2).
student(marina, 1).
spisok:- student(X, 1), write (X), nl, fail.
Пропишите цель:
GOAL
write("Список студентов 1-курса"), nl, spisok.
Пролог должен вывести (рис. 1):
lena
dima
marina
Рис. 1. Вывод результата программы
Создайте новый проект и сформируйте список из N элементов, начиная с 2. Каждый следующий на 4 больше предыдущего.
DOMAINS
list=integer*
PREDICATES
genl(integer, integer, list)
CLAUSES
genl(N2, N2, []):-!.
genl(N1, N2, [N1|L]):- N1<N2, N=N1+4, genl(N, N2, L).
GOAL
write(“N=”), readint(N), K=4*(N+1)-2, genl(2, K, L), write(L), nl.
Результат выполнения программы:
N=5
[2, 6, 10, 14, 18]
Список представляет собой последовательность, состоящую из любого количества элементов, таких как 2, 6, 10, 14, 18. Список можно рассматривать как состоящий из таких компонентов, как голова списка и хвостовая часть списка. Головой списка является - 2, а хвостовой частью - следующий список [6, 10, 14, 18] (рис. 2).
Рис. 2. Древовидное представление списка [2, 6, 10, 14, 18]
Затем сформируем список последовательных натуральных чисел от 4 до 20 и найдём количество его элементов.
DOMAINS
list=integer*
PREDICATES
genl1(integer, integer, list)
len(integer, list)
CLAUSES
genl1(N2, N2, []):-!.
genl1(N1, N2, [N1|L]):- N1<N2, N=N1+1, genl1(N, N2, L).
len(0, []).
len(X, [_|L]):- len(X1, L), X=X1+1.
GOAL
genl1(4, 21, L), write(L), nl, len(X, L), write(“Количество элементов =”, X), nl.
Результат выполнения программы:
[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Количество элементов=17
Теперь определим, содержится ли введенное число Х в заданном списке L.
Напишите следующий программный код:
DOMAINS
list=integer*
PREDICATES
member(integer, list)
CLAUSES
member(X, [X|_]):- write(“yes”), !.
member(X, []):- write(“no”), !.
member(X, [_|L]):- member(X, L).
GOAL
L=[1, 2, 3, 4], write(L), nl, write(“X=”, X), readint(X), member(X, L), nl.
Результат выполнения программы:
1-й случай:
[1, 2, 3, 4]
X=3
yes
2-й случай:
[1, 2, 3, 4]
X=5
no
Сформируйте списки L1=[1, 2, 3], L2=[10, 11, 12, 13, 14, 15] и объединить их в список L3.
DOMAINS
list=integer*
PREDICATES
genl1(integer, integer, list)
append(list, list, list)
CLAUSES
genl1(N2, N2, []):-!.
genl1(N1, N2, [N1|L]):- N1<N2, N=N1+1, genl1(N, N2, L).
append([], L, L).
append([X|L1], L2, [X|L3]):- append(L1, L2, L3).
GOAL
genl1(1, 4, L1), write(“L1=”, L1), nl,
genl1(10, 16, L2), write(“L2=”, L2), nl,
append(L1, L2, L3), write(“L3=”, L3), nl.
Результат выполнения программы:
L1=[1, 2, 3]
L2=[10, 11, 12, 13, 14, 15]
L3=[1, 2, 3, 10, 11, 12, 13, 14, 15]
Подсчет элементов списка
Теперь рассмотрим пример, демонстрирующий, как можно узнать, сколько элементов находится в списке, т.е. какова его длина.
Длина [] - 0.
Длина любого другого списка - 1 плюс длина его хвостoвой части.
Реализуйте программный код:
DOMAINS
list=integer*
PREDICATES
length_of(list, integer)
CLAUSES
length_of([], 0).
length_of([_|T], L):- length_of(T, TailLength), L=TailLength+1.
Второе предложение: кардинально, [_|T] будет соответствовать любому непустому списку, связывая T с хвостовой частью списка. Значение головы незначительно.
Пропишите цель:
length_of([1, 2, 3], L).
Данная цель будет соответствовать второму предложению, с T = [2, 3]. Следующий шаг должен вычислить длину T. Когда это сделано, TailLength получит значение 2, и компьютер может тогда добавить 1 к этому и связать L с 3.
Сравните результаты ответа (рис. 3):
Рис. 3. Вывод длины списка
Другими словами, length_of вызывается рекурсивно. Эта цель соответствует второму предложению.
Повторно, TailLength в цели не будет сталкиваться с TailLength в предложении, потому что каждое рекурсивное обращение предложения имеет его собственный набор переменных.
Теперь проблема состоит в том, чтобы найти длину [3], которая будет 1 и затем добавлять 1 к ней, который получить длину [2, 3], которая будет 2. Аналогично, length_of вызовет себя рекурсивно снова, чтобы получить длину [3]. Хвост [3] - [], так что T связан с [], и проблема состоит в том, чтобы получить длину [], затем добавлять 1 к этому, давая длину [3]. На сей раз это просто.
Цель
length_of([], TailLength)
соответствует первому предложению, связывая TailLength с 0. Так что теперь компьютер может добавить 1 к тому, что, дает длину [3], и возвращается к предложению запроса. Что, в свою очередь, добавит 1 снова, давая длину [2, 3], и возвратится к предложению, которое вызывало это. Оно добавит 1 снова, давая длину [1, 2, 3].
На следующей краткой иллюстрации мы будем суммировать запросы. Мы использовали приписки, чтобы указать, что переменные в различных предложениях или различных обращениях того же самого предложения являются отличными.
length_of([1, 2, 3], L1).
length_of([2, 3], L2).
length_of([3], L3).
length_of([], 0).
L3 = 0+1 = 1.
L2 = L3+1 = 2.
L1 = L2+1 = 3.
Изменение списка
Иногда необходимо из списка создать другой. Это осуществляется последовательной обработкой элементов списка, заменяя каждый вычисленной величиной.
Следующий пример демонстрирует просмотр списка чисел и формирование нового, не учитывая отрицательные числа:
DOMAINS
list=integer*
PREDICATES
discard_negatives(list, list)
CLAUSES
discard_negatives([], []).
discard_negatives([H|T], ProsessedTail):- H<0, !, discard_negatives(T, ProsessedTail).
discard_negatives([H|T], [H|ProsessedTail]):- discard_negatives(T, ProsessedTail).
GOAL
discard_negatives([2, -45, 3, 468], X).
Пролог выдаст ответ (рис. 4):
X=[2, 3, 468].
Рис. 4. Изменнённый список
Членство в списке
Предположим, что Вы имеете список с именами Джон, Леонард, Эрик, и Фрэнк и хотели бы использовать Visual Prolog для исследования, есть ли данное имя в этом списке. Другими словами, Вы должны выразить отношение «членство» между двумя параметрами: имя и список имен. Это соответствует предикату member(name, namelist).
Опишите разделы доменов, предикатов, предложений и задайте цель:
DOMAINS
namelist=name*
name=symbol
PREDICATES
nondeterm member(name, namelist)
CLAUSES
member(Name, [Name|_]).
member(Name, [_|Tail]):-
member(Name, Tail).
GOAL
member(john, [john, leonard, eric, frank]).
В Программе, первое предложение исследует голову списка. Если голова списка равна имени, которое Вы ищете, то Вы можете заключить, что имя - член списка. Так как хвостовая часть списка неинтересна, она обозначена анонимной переменной. Благодаря этому первому предложению, цель member(john, [john, leonard, eric, frank]) удовлетворена.
Пролог ответит yes (рис. 5).
Рис. 5. Подтверждение членства в списке
Если голова списка не равна Name, Вы должны исследовать, может ли Name быть найдено в хвостовой части списка.
Второе предложение member касается этих отношений. На Прологе:
member(Name, [_|Tail]) :- member(Name, Tail).
Рекурсия с процедурной точки зрения
В следующем примере мы создадим предикат, чтобы добавить в список в окончание другого. Определим предикат с тремя параметрами: append(list1, list2, list3).
List1 и List2 используются, чтобы сформировать List3. Еще раз используем рекурсию.
Если List1 пуст, результат добавления в конец List1 и List2 будет тот же самый как List2. На Прологе:
append([], List2, List2).
Если List1 не пуст, Вы можете комбинировать List1 и List2, чтобы формировать List3, делая голову List1 головой List3. (В следующем коде, переменная H используется как голова и List1, и List3.) Хвостовая часть List3 третьего уровня, который составлен из остальной части List1 (а именно, L1) и всего List2. На Прологе:
append([H|L1], List2, [H|L3]):-
append(L1, List2, L3).
Предикат append работает следующим образом: В то время как List1 не пуст, рекурсивное правило передает один элемент одновременно List3. Когда List1 пуст, первое предложение гарантирует, что List2 связывается с задней частью List3.
Загрузите программу:
DOMAINS
integerlist=integer*
PREDICATES
append(integerlist, integerlist, integerlist)
CLAUSES
append([], List, List).
append([H|L1], List2, [H|L3]):-
append(L1, List2, L3).
Теперь выполните программу со следующей целью, Пролог должен вывести следующий ответ (рис. 6):
append([1, 2, 3], [5, 6], L).
Рис. 6. Вывод списка
Задайте следующую цель (рис. 7):
append([1, 2], [3], L), append(L, L, LL).
Рис. 7. Вывод списков
Составные списки
Пример объявления домена для списка, который может содержать целое число, символ, строку, или список любого из них:
DOMAINS /* функторы l, i, c, и s */
llist = l(list); i(integer); c(char); s(string)
list = llist*
Список
[2, 9, ["food", "goo"], "new"] /* запись списка не на языке Пролог*/
записывается как
[i(2), i(9), l([s("food"), s("goo")]), s("new")] /* запись списка на языке Пролог*/
Рассмотрим пример, который показывает, как использовать это объявление домена в типичной программе манипуляции списка.
DOMAINS
llist = l(list); i(integer); c(char); s(string)
list = llist*
PREDICATES
append(list, list, list)
CLAUSES
append([], L, L).
append([X|L1], L2, [X|L3]):-
append(L1, L2, L3).
GOAL
append([s(likes), l([s(bill), s(mary)])], [s(bill), s(sue)], Ans),
write(“FIRST LIST: ”, Ans, “\n\n”),
append([l([s(“This”), s(“is”), s(“a”), s(“list”)]), s(bee)], [c(‘c’)], Ans2),
write(“SECOND LIST: ”, Ans2, ‘\n’).
Пролог выдаст ответ (рис. 8):
Рис. 8. Результат программы
Удаление элемента из списка
Разберем задачу удаления из списка, элементами которого являются названия дней недели, указанного элемента.
Создайте новый проект:
DOMAINS
list=symbol*
PREDICATES
del(symbol, list, list)
CLAUSES
del(X, [X|L], L).
del(X, [Y|L], [Y|L1]):- del(X, L, L1).
GOAL
L=[пн, вт, ср, чт, пт, сб, вс], write(“L=”, L), nl,
write(“X=”), readln(X),
del(X, L, L1), write(“L1=”, L1), !;
write(“Элемент отсутствует в списке”), nl.
Результат выполнения программы (рис. 9, 10):
1-й случай:
L=["пн","вт","ср","чт","пт","сб","вс"]
X=ср
L1=["пн","вт","чт","пт","сб","вс"]
Рис. 9. Удаление из списка дней недели среды
2-й случай
L=["пн","вт","ср","чт","пт","сб","вс"]
X=пр
Элемент отсутствует в списке
Рис. 10. Вывод результата программы при удалении несуществующего в списке элемента
Вставим в список имен новый элемент, значение которого вводится с клавиатуры. Необходимо вывести все возможные варианты вставок.
Создайте проект следующего содержания:
DOMAINS
list=symbol*
PREDICATES
del(symbol, list, list)
ins(symbol, list, list)
CLAUSES
del(X, [X|L], L).
del(X, [Y|L], [Y|L1]):- del(X, L, L1).
ins(X, L1, L):- del(X, L, L1).
GOAL
L=[olga, oksana, toma, dima], write(“L=”, L), nl,
write(“X=”), readln(X),
ins(X, L, L1), write(“L1=”, L1), nl, fail.
Результат выполнения программы:
L=["olga","oksana","toma","dima"]
X=vera
L1=["vera","olga","oksana","toma","dima"]
L1=["olga","vera","oksana","toma","dima"]
L1=["olga","oksana","vera","toma","dima"]
L1=["olga","oksana","toma","vera","dima"]
L1=["olga","oksana","toma","dima","vera"]
