Лекция_9

advertisement
Пример разработки системы
Пример разработки системы для
построения и обхода дерева решений
На рисунке показан фрагмент дерева решений,
позволяющий выбрать нужную персональную ЭВМ
среди компьютеров, выпущенных в 1986 г.
Пример разработки системы для
построения и обхода дерева решений
Создать описания дерева решений позволяют факты вида
верш(Верш_ид, Вопрос,Дуги)
Верш_ид - идентифицирует вершины,
Вопрос - это вопрос или решение,
Дуги - список дуг, выходящих из вершины.
Вопрос определяется списком строк, каждая из которых имеет тип "строка".
Дуга представляется термином (Отв, Верш_ид), означающим, что при ответе Отв
следует перейти к вершине с идентификатором Верш_ид.
Вершина "Портативная?" записывается следующим образом:
верш(1,[Портативная?],[дуга(да,2),дуга(нет,3)])
Т.о. мы присвоили идентификаторы 1,2 и 3 вершинам "Портативная?", "Карманная?" и
"IBM-совместимая" соответственно.
Систему будем разрабатывать в 2 этапа.
Сначала создадим удобный интерфейс для пользователя, а затем напишем целевые
утверждения для построения и обхода дерева решений.
Создание интерфейса пользователя
Меню возможных действий пользователя выглядит
следущим образом:
меню:Каждое действие будет осуществляться
repeat,nl,
соответствующим утверждением выполнить.
write ('Хотите ли вы'),nl,
Утверждение выполнить должно
write ('1)загрузить дерево'),nl,
закончиться возникновением состояния неудачи
write ('2)сохранить дерево'),nl,
для всех вариантов от 1 до 7, чтобы после
write ('3)добавить вершину'),nl,
совершения требуемого действия на экране
write ('4)убрать вершину'),nl,
снова показалось меню.
write ('5)добавить дугу'),nl,
write ('6)убрать дугу'),nl,
write ('7)совершить обход дерева'),nl,
write ('8)стоп'),nl,
write ('Введите 1-9'),nl,
write (' '),nl,
readb (Вариант),nl,
выполнить (Вариант).
Создание интерфейса пользователя
(дерево)
Дерево загружается с
помощью предиката reconsult:
выполнить (1):write('имя файла?'),
write(Ф),nl,
write('чтение файла...'),
reconsult(Ф),
write('...закончилось'),nl,!,fail.
Для того чтобы сохранить дерево, прежде всего надо
поместить специальный маркер в конец списка вершин. Если
маркер становится аргументом предиката сохр_верш, то
данный предикат убирает маркер и попытки доказательства
заканчиваются успехом.
В противном случае сохр_верш записывает вершину в файле и
доказательство завершается неудачей. Начинает работать
механизм возврата, выбирается следующая вершина, которая
сохраняется в том случае, если не является маркером.
выполнить(2):write('имя файла?'),
readb(Ф),
open(Ф,блок,write),!,nl,
write('запись в файл...'),
write(Ф),
assrtz(верш(-1,-1,-1,)),
верш(Вид,В,Дуги),
сохр_верш(Вид,В,Дуги,блок),
write('...закончилась'),nl,
close(блок),!,fail.
Создание интерфейса пользователя
(вершины)
Добавить вершину можно при условии, что ее
дуги не ведут к ней самой. Другими словами,
добавление вершины не должно приводить к
возникновению циклов. Целевое утверждение
цикл(Вид,Дуги) доказывается успешно, если одна
из Дуг ведет к Вид, и заканчивается неудачей в
противном случае. При успешном доказательстве
цели цикл определяется и пользователю
показывается циклический путь. Целевое
утверждение чит_нов_ид вводит новый
идентификатор вершины, чит_вопрос вводит
вопрос, а чит_дуги вводит список дуг.
выполнить(3):чит_нов_ид(Вид),
write('Введите вопрос(в конце напишите слово
'конец'):'),nl,
чит_вопрос(В),
чит_дуги(Дуги),
not(цикл(Вид,Дуги)),
assertz(верш(Вид,В,Дуги)),!,fail.
Вершина уничтожается
следующим образом:
выполнить(4):чит_стар_ид(Вид),
уничт_верш(Вид),!,fail.
Чтобы сохранить вершину, мы
записываем ее в память при условии,
что она не является специальным
маркером:
сохр_верш(-1,-1,-1,_):-!,
retract(верш(-1,-1,-1)).
сохр_верш(Вид,В,Дуги,Б):writeq(Б,верш(Вид,В,Дуги)),
write(Б,'.'),
nl(Б),!,fail.
Создание интерфейса пользователя
(дуги)
Дуга, выходящая из вершины,
добавляется в том случае, если при
этом не образуется цикл:
выполнить(5):write('Из вершины'),
чит_стар_ид(Вид),
чит_дугу(Дуга),
not(цикл(Вид,[Дуга])),
доб_дугу(Вид,Дуга),!,fail.
Выход из меню:
выполнить(8):-nl,write('Досвидания'),nl,!.
Уничтожение дуги:
выполнить(6):чит_стар_ид(Вид),
чит_дугу(Дуга),
уничт_дугу(Вид,Дуга),!,fail.
Обход дерева:
выполнить(7):чит_стар_ид(Вид),
обход(Вид),!,fail.
уничт_дугу(Вид,Дуга),!,fail.
Создание интерфейса пользователя
(вопросы)
Чтобы прочитать заданный вопрос,
надо читать все строки на экране до
тех пор, пока не встретится строка
'конец'. Для чтения строк
используется встроенный предикат
readline.
чит_вопрос(В):write('>'),
readline(Строка),
продолжать(Строка,В).
продолжать('конец',[]):-!.
продолжать(Строка,[Строка|В]):чит_вопрос(В).
Вопрос выводится на дисплей
построчно:
запис_вопр([]):-!.
запис_вопр([H|T]):nl,write(H),
запис_вопр(T).
Описание целевых утверждений для
построения и обхода дерева решений
При уничтожении вершины мы
уничтожаем также все входящие в нее
дуги:
уничт_верш(Вид):retract(верш(Вид,В,Дуги)),
уничт_все_вх_дуги(Вид),
nl,write('вершина уничтожена'),nl.
Чтобы получить доступ ко всем вершинам,
помещаем маркер в конец списка вершин и
используем механизм возврата:
уничт_все_вх_дуги(Вид):assertz(верш(-1,-1,-1)),
retract(верш(Вид2,В,Дуги)),
уничт2(Вид2,В,Дуги,Вид),!.
уничт2(-1,-1,-1,_).
уничт2(Вид2,В,Дуги,Вид):уничт_список(Дуги,дуга(Д,Вид),Дуги2),
assertz(верш(Вид2,В,Дуги2)),!,
fail.
Описание целевых утверждений для
построения и обхода дерева решений
Целевое утверждение
уничт_список(Дуги,дуга(Д,Вид),Дуги2)
уничтожает дугу дуга(Д,Вид) в списке Дуги,
в результате чего получается список Дуги2.
Предикат уничт_список импортируется из
модуля список.
Добавляя дугу, следует проверить, не
содержится ли она уже в списке дуг:
доб_дугу(Вид,Д):верш(Вид,В,Дуги),
принадлежит(Д,Дуги),!,
nl,write('уже существует!!'),nl.
доб_дугу(Вид,дуга(Д,Вид2)):верш(Вид2,_,_),
retract(верш(Вид,В,Дуги)),
assertz(верш(Вид,В,[дуга(Д,Вид2)|Дуги])),
nl,write('дуга добавлена'),nl.
Утверждение уничт_дугу позволяет
уничтожить дугу Д, выходящую из
вершины с идентификатором Вид:
уничт_дугу(Вид,Д):retract(верш(Вид,В,Дуги)),
assertz(верш(Вид,В,Дуги2)),
nl,write('дуга уничтожена'),nl.
Описание целевых утверждений для
построения и обхода дерева решений
Будем говорить, что вершина Вид1 ведет к вершине Вид2,
если имеет место одна из трех ситуаций: Вид2 есть
Вид1, Вид2 находится в конце дуги, выходящей из Вид1,
или же вершина, находящаяся в конце дуги, выходящей
из Вид1, ведет к Вид2.
Доказательство целевого утверждения ведет
заканчивается успехом, если существует путь, ведущий
от дуги (первого аргумента утверждения) к вершине
(второму аргументу).
В противном случае доказательство завершается
неудачей. При успешном доказательстве утверждения
ведет в качестве третьего аргумента возвращается
искомый путь.
ведет([дуга(Д,Вид)|Т],Вид2,П):вести(Вид,Вид2,П),!.
ведет([_|Т],Вид2,П):-ведет(Т,Вид2,П).
вести(Вид,Вид,[Вид,Вид]):вести(Вид1,Вид2,[Вид1,Вид2]):верш(Вид1,В,Дуги),
принадлежит(дуга(Д,Вид2),Дуги),!.
вести(Вид1,Вид2,[Вид1|П]):верш(Вид1,В,Дуги),
ведет(Дуги,Вид2,П).
Для того чтобы пройти по дереву, начиная с вершины
СВид, нужно вывести на экран вопрос, содержащийся
в вершине.
Если вершина не имеет исходящих дуг, работа
заканчивается. В противном случае система читает
ответ пользователя и использует предикат
принадлежит для выборки следующей вершины:
обход(СВид):верш(СВид,В,Дуги),
обраб_дуги(Дуги,В).
обраб_дуги([],В):-!,
писать_вопрос(В),
обраб_дуги(Дуги,В):ввести_ответ(В,Дуги,Отв),
принадлежит(дуга(Отв,Вид),Дуги),
обход(Вид).
Описание целевых утверждений для
построения и обхода дерева решений
Ответ пользователя на вопрос,
содержащийся в вершине,
вводится с помощью следующего
утверждения:
ввести_ответ(В,Дуги,Отв):repeat,
писать_вопрос(В),
readb(Отв),
принадлежит(дуга(Отв,_),Дуги),!.
Предикат ввести_ответ
гарантирует совпадение ответа
пользователя хотя бы с одним
ответом, расположенным на дуге.
Последнее, что нам осталось сделать - это
осуществить импорт предиката уничт_список из
модуля список:
:-import(список,[уничт_список]).
Помимо предиката уничт_список, модуль список
содержит и другие целевые утверждения для
обработки списков.
:-module(список).
:-visa([visa,secure],[присоединить,...,уничт_список,...])
...
уничт_список([],Е,[]):-!.
уничт_список([Е|Т],Е,Т2):-!,
уничт_список(Т,Е,Т2).
уничт_список([Н|Т],Е,[Н|Т2]):уничт_список(Т,Е,Т2).
...
:-endmodule(список).
Пример диалога пользователя с
разработанной системой
OK, дерево Пролога
?-меню.
Хотите ли вы:
1)загрузить дерево
2)сохранить дерево
3)добавить вершину
4)убрать вершину
5)добавить дугу
6)убрать дугу
7)совершить обход дерева
8)выйти из системы
Введите 1-9? 3
Идентификатор новой вершины? IBM_портативная.
Введите вопрос(в конце напишите слово 'конец'):
>>Вы должны купить портативную ЭВМ фирмы IBM
>>Она стоит 1632 фунта стерлингов,
>>использует процессор 8088, имеет 256К
>>оперативной памяти
>>
>>конец
Сколько дуг имеет вершина? 0
Хотите ли вы:
[n.меню
Введите 1-9? 3
Идентификатор новой вершины? sharp_pc7000
Введите вопрос (Вконце напишите слово 'конец'):
>>Вы должны купить портативную ЭВМ фирмы sharp_pc7000
>>Она стоит 2577 фунта стерлингов,
>>использует процессор 8088, имеет 320К
>>оперативной памяти
>>
>>конец
Сколько дуг имеет вершина? 0
Хотите ли вы:
[меню
Введите 1-9? 3
Идентификатор новой вершины? цена
Введите вопрос (Вконце напишите слово 'конец'):
>>Сколько денег вы можете затратить на
>>покупку:
>>а.2000 фунтов стерлингов
>>б.3000 фунтов стерлингов
>>?
>>конец
Пример диалога пользователя с
разработанной системой
Сколько дуг имеет вершина? 2
Дуга1
Ответ? а
К вершине IBM_портативная
Дуга2
Ответ? б
К вершине? sharp_pc7000
Хотите ли вы:
[меню
Введите 1-9? 3
Идентификатор новой вершины? карманная
Введите вопрос (В конце напишите слово 'конец'):
>>Должна ли ЭВМ помещаться в вашем кармане?
>>конец
Сколько дуг имеет вершина? 1
Дуга1
Ответ? нет
К вершине? цена
И т.д.
Download