Основы логического программирования в среде Турбо Пролог. Учебное пособие по курсу «Экспертные системы»
.pdfстрок, содержащих прописные и строчные буквы. Второй предикат рекурсивно вызывает себя, каждый раз проверяя, не является ли первый переданный символ первым в строке с заглавными буквами (frontchar), и если это так, то второй символ должен соответствовать первому в строке со строчными буквами для успешного согласования предиката – первое предложение. Если первый символ содержится в строке со строчными буквами, то предикат также успешно согласуется (преобразование не требуется) – второе предложение. Третье предложение осуществляет рекурсивный вызов, каждый раз отсекая первые буквы в строках с символами (3 и 4 параметры), что позволяет первым двум предложениям проверить соответствие передаваемых символов каждому из служебных строк.
lastchar(townname,char) успешно согласуется в случае, когда символ char является последней буквой в названии города townname, причем
последняя буква определяется в соответствии с правилами игры: если остаток от имени города не является пустой строкой, либо мягким или твердым знаком, осуществляется рекурсивный вызов, причем название города укорачивается на одну букву с начала (первое предложение), в противном случае искомым символом является первый символ в остатке от названия города (второе предложение).
nexttown(townname, townname, mode) успешен в случае, если второй
переданный в качестве параметра город является следующим по отношению к первому, в соответствии с правилами игры (второе предложение). Если город не соответствует условию, то, помимо неуспешности самого предиката, выдается также диагностическое сообщение («Вы нарушаете правила…»), причем сообщение выдается только тогда, когда предикат работает в режиме «talk» (определяется третьим параметром). В режиме
«silent» диагностических сообщений не выдается – в этом режиме ком-
пьютер перебирает варианты своего хода. Первое предложение введено для осуществления выхода из игры вводом ключевого слова «end».
checktown(townname) – проверка введенного пользователем города
на достоверность – предикат успешен, если названный пользователем город содержится в базе знаний системы (третье предложение). Первое предложение – отработка ключевого слова «end» (см. предыдущий предикат). Если город уже назывался, выводится соответствующее сообщение, а предикат неуспешен (второе предложение). То же самое в случае, если введена пустая строка (пятое предложение). Во всех случаях неудачи выводится предложение пользователю попробовать еще раз (шестое предложение). Если система впервые встречает тот или иной город, она запрашивает подтверждение (четвертое предложение), необходимое для
51
достоверного ввода нового города в базу знаний и если пользователь уверен в своем выборе («1»), пополняет базу знаний городов.
getusertown(townname) – повторяет запрос на ввод города пользовате-
лем до тех пор, пока введенный город не пройдет проверку предикатом checktown, введенный при этом город возвращается в качестве результата. getnextusertown(townname, townname) – то же что и getusertown,
только возвращаемый город (второй параметр) проходит дополнительную проверку предикатом nexttown.
userturn(townname) – отработка хода пользователя: запрос следующего города, запись введенного города в базу данных уже названных городов, передача хода компьютеру.
pc turn(townname) – отработка хода компьютера: подбор следующего города, вывод подобранного города на экран с пояснением «Мой ход», запись города в базу данных уже названных городов, передача хода пользователю (второе предложение). Альтернативная ветвь (третье предложение) соответствует случаю, когда компьютер не смог подобрать подходящий город, при этом предикат успешен (необходимо для нормальной отработки предиката game, а затем и goal – завершения игры), а пользователю предлагается ввести город, который мог бы быть следующим (необходимо для самообучения системы). Первое предложение, как и третье, ведет к успешности предиката без передачи хода пользователю.
game – собственно сама игра: выводит сообщение о начале игры, предлагаетпользователюсделатьпервыйход,запоминаетпервыйвведенный город как уже названный, передает ход компьютеру (pc turn), выводит сообщение об окончании игры. Этот предикат всегда успешен. Заметим, что передача хода компьютеру приводит к последовательности рекурсив-
ных вызовов (pc turn–userturn–pc turn–…) до тех пор, пока либо человек не введет «end», либо компьютер не сможет назвать следующий город, на этом рекурсия заканчивается – происходит возврат в game.
safeconsult(string) – в случае если существует файл с базой знаний городов, происходит очистка внутренней базы знаний городов (towns) и ее загрузка из файла, в противном случае – ничего не происходит. Предикат всегда успешен.
rep eat был описан ранее (см. п. 2.1.).
goal – основная цель программы: загрузка базы знаний городов (при наличии), затем цикл: очистка базы знаний названных городов, выполнение предиката game, запись базы знаний городов (towns), запрос к пользователю «Будете еще играть?», если да («1»), то неуспех, приводящий к повторению цикла, в противном случае – успех – конец программы.
52
Задания для самостояте льной работы
1.Создайте программу, определяющую по ряду ключевых признаков животных (растения и т. д.).
2.Создайте программу, позволяющую диагностировать заболевание по симптомам.
3.Создайте программу, позволяющую диагностировать неисправность какого-либотехническисложногоустройства(велосипед,автомобиль, компьютер, телевизор и т. д.).
4.Создайте программу, помогающую школьнику определиться с выбором будущей профессии.
5.Создайте программу, решающую задачу раскраски карты. Задача заключается в следующем. Имеется набор пар, в котором первая компонента представляет собой название страны, вторая – список стран, граничащих со страной, чье название находится в первой компоненте. Требуется сопоставить каждой стране цвет так, чтобы ни одна пара соседних стран не была окрашена в одинаковые цвета. При этом разрешается использовать не более четырех цветов.
6.Создайте программу, осуществляющую поиск пути в лабиринте. Задача заключается в следующем. Имеется описание лабиринта в виде набора координат стен, а также координаты текущей позиции в лабиринте и координаты выхода. Требуется найти путь от текущей позиции до выхода.
7.Создайтепрограмму,разгадывающуюсекретныйкодпоправиламигры «Быки и коровы»: человек выбирает секретный код, представляющий из себя последовательность из N различных десятичных цифр (обычно N устанавливается равным 4). Компьютер пытается угадать задуманный код и спрашивает человека о числе «быков» (число «быков» – количество совпадающих цифр в одинаковых позициях предполагаемого и задуманного кодов; число «коров» – количество совпадающих цифр, входящих в предполагаемый и задуманный код, но находящихся на разных позициях). Код угадан, если число быков равно N .
8.Создать программу, решающую задачу о двух кувшинах. Переливая воду из кувшина в кувшин, наливая полный кувшин или выливая воду из него на землю, необходимо добиться, чтобы один из кувшинов содержал 1 литр. Емкости кувшинов 5 и 2 литра.
53
Приложение 1
Стандартные пре дикаты
При описании предикатов приняты следующие правила. Сначала записываются имя предиката и способ его вызова, далее – типы параметров в соответствующих позициях предиката, затем – описание шаблонов для всех параметров (i – вход (inp ut), o – выход (outp ut)). Каждый шаблон заключается в круглые скобки.
Предикаты ввода
readln(StringVariable) (string) (o) – считывает символы с входного
потока (по умолчанию, с клавиатуры) до нажатия клавиши ВВОД. Введенные символы присваиваются переменной Variable, которая должна быть строковой (string) или символьной (symbol).
readint(IntgVariable) (integer) (o) – считывает целое число с входно-
го потока и присваивает его переменной Variable. readreal(RealVariable) (real) (o) – считывает вещественное число с
входного потока и присваивает его переменной Variable. readchar(CharVariable) (char) (o) – считывает один символ с вход-
ного потока и присваивает его переменной Variable. file str(DosFil eName, StringVariable) (string, string) (i,o) (i,i) – счи-
тывает данные из файла с указанным именем (DosFileN ame) и помещает их в StringVariable (переменная StringVariable должна быть символьной
или строковой) либо записывает данные из переменной в файл. Симво-
лом конца файла является Ctrl–Z (ASCII – 26).
inkey(CharVariable) (char) (o) – считывает с клавиатуры один сим-
вол и помещает его в CharVariable.
keypress ed – предикат успешен, если в момент вызова нажата какаялибо клавиша.
unreadchar(CharToBePushedBack) (сhar) (i) – помещает символ
CharToBePushedBack в буфер клавиатуры. Действие предиката эквива-
лентно нажатию соответствующей клавиши на клавиатуре. readterm(Domain, Variable) (DomainN ame, Domain) (i,o) читает
терм, объявленный с именем Domain.
Предикаты вывода
nl – возврат каретки и перевод строки.
write(Arg1, Arg2, …) ((i)*) – выводит значения аргументов в за-
данное активное окно на текущее устройство (по умолчанию, на экран
54
дисплея). Аргументы Arg1, Arg2, … могут быть константами или переменными, которым заранее присвоены требуемые значения. В предикате write можно использовать управляющие символьные константы, начи-
нающиеся со знака \.
\nnn – символ, имеющий вASCII – код числа nnn; \n – возврат каретки и перевод строки;
\t – табуляция.
writef(FormatString, Arg1, …, Argn) (i, (i)*) – осуществляет фор-
матированный вывод в соответствии с форматом, заданным управляющей строкой (FormatString). Управляющая строка содержит объекты трех типов: обычные символы, которые просто выводятся на экран дисплея; управляющие символьные константы (см. предикат write); спецификации преобразования, каждая из которых вызывает вывод значения очередного аргумента из последующего списка.
Каждая спецификация преобразования начинается со знака % и заканчивается некоторым символом, задающим преобразование. Между знаком % и символом преобразования могут встречаться (строго в приведенной последовательности): знак минус ( – ), указывающий, что преобразованный параметр должен быть выровнен влево в своем поле; число, задающее минимальный размер поля; точка, отделяющая размер поля от последующего числа; число, задающее максимальное количество символов, которое нужно вывести или же число цифр, которое необходимо вывести справа от десятичной точки (в значениях float и double).
Далее записывается один из следующих символов преобразования (в скобках указано, для каких типов данных применим тот или иной символ):
%d – значением является десятичное целое число (char и integer);
%u – значением является беззнаковое целое число (char и integer);
%X – значением является длинное шестнадцатеричное число
(string);
%x – значением является шестнадцатеричное число (char и integer);
%s – значением является строка символов (symbol и string);
%c – значением является символ (char и integer);
%e – значением является вещественное десятичное число в экспоненциальном формате;
%f – значением является вещественное десятичное число с плавающей точкой;
%g – используется как %e и %f, исключает вывод незначащих нулей
(real – по умолчанию).
55
Предикаты для работы с файлами
openread(SymbolicFileName, DosFil eName) (file, string) (i,i) – файл с заданным физическим именем открывается для чтения, и ему ставится в соответствие логическое имя. Например, op enread(myfile, «p rim. p ro»).
openwrite(SymbolicFileName, DosFil eName) (file, string) (i,i) – файл с заданным физическим именем открывается для записи, и ему ставится в соответствие логическое имя.
openappend(SymbolicFileName, DosFil eName) (file, string) (i,i) –
файл с заданным физическим именем открывается для дополнения, и ему ставится в соответствие логическое имя.
openmodify(SymbolicFileName, DosFil eName) (file, string) (i,i) –
файл с заданным физическим именем открывается для модификаций (чтения и записи), и ему ставится в соответствие логическое имя.
readdevice(SymbolicFileName) (file) (i) (o) – перераспределяет стан-
дартный входной поток на файл с заданным логическим именем, который открывается для чтения.
writedevice(SymbolicFileName) (file) (i) (o) – перераспределяет стан-
дартный выходной поток на файл с заданным логическим именем, который открывается для записи или добавления.
filemode(SymbolicFileName, FileMode) (file, integer) (i,i) (i,o) – поз-
воляет узнать или задать режим доступа к файлу. FileMode – задает или принимает режим работы с файлом SymbolicFileN ame. FileMode может
принимать одно из двух значений. Значение «o» соответствует двоичному (бинарному) режиму работы с файлом, а значение «1» — текстовому режиму. В текстовом режиме к строкам добавляются символы возврата каретки и перевода строки. Чаще используется текстовый режим, поскольку при работе в двоичном режиме данные можно записывать только посимвольно.
closefile(SymbolicFileName) (file) (i) – файл с указанным логическим именем закрывается.
filepos(SymbolicFileName, FilePosi tion,Mode) (file, real, integer) (i,i,i) (i,o,i) – устанавливает текущий указатель файла (SymbolicFileN ame) на заданную позицию (FilePosition) в указанном файле. Режим (Mode) опре-
деляет, как будет отсчитываться позиция:
0 (нуль) – позиция отсчитывается от начала файла; 1 – от текущего значения указателя; 2 – от конца файла.
56
Этот предикат используется для прямого доступа к файлу. eof(SymbolicFileName) (file) (i) – проверяет, находится ли указатель
текущей позиции файла в его конце (если да, то eof завершается успешно; в противном случае – нет).
flush(SymbolicFileName) (file) (i) – используется для принудительной записи в файл содержимого внутреннего буфера, выделенного для файла, заданного в SymbolicFileN ame. Обычно используется при работе с принтером.
existfile(DosFil eName) (string) (i) – завершается успешно, если файл
с именем DosFileN ame есть в текущей директории. deletefile(DosFil eName) (string) (i) – удаляет файл DosFileN ame из
текущей директории.
renamefile(OldDosFil eName, NewDosFil eName) (string, string) (i,i) –
переименовывает файл (OldDosFileN ame на N ewDosFileN ame). disk(DosPath) (string) (i) (o) – устанавливает или считывает текущий
каталог (DosPath).
Предикаты обработки строк
frontchar(String, FrontChar, RestString) (string, char, string) (i,o,o) (i,i,o) (i,o,i) (i,i,i) (o,i,i) – работает в соответствии с уравнением: String = (объединение FrontChar и RestString), где FrontChar – первый символ строки String, а RestString – остаток строки String без первого символа. fronttoken(String,Token,RestString)(string,string,string)(i,o,o)(i,i,o) (i,o,i) (i,i,i) (o,i,i) – подобен предикату frontchar, однако второй параметр имеет тип string и принимает первый атом, входящий в строку. Атом – это или идентификатор, или беззнаковое число (целое или веществен-
ное), или символ.
frontstr (N, Inpstring, StartString, RestString) (integer, string, string, string) (i,i,o,o) – назначает первые N символов Inp string строке StartString,
а остальные – RestString.
concat(String1, String2, String3) (string, string, string) (i,i,o) (i,o,i) (o,i,i) (i,i,i) – утверждает, что String3 – это объединение строк String1 и String2 (String3 = String1 + String2).
str_len(String, Length) (string, integer) (i,i) (i,o) (o,i) – возвращает длину строки String или проверяет, имеет ли она длину Length.
isname(String) (string) (i) – проверяет строку String на синтаксис
Пролога. Истинен, если String представляет собой имя, доступное в Тур-
57
бо Прологе. Выполняется только для последовательности символов, состоящей из латинских букв.
format(OutputVariable, FormatString, Variable|Constant*) (o,i,i) –
выводит по формату FormatString список вывода Variable|Constant* в
строковую переменную Outp utVariable. (Структура FormatString рассмотрена в описании предиката writef).
Предикаты для преобразования типов
char_int(CharParam, IntgParam) (char, integer) (i,o) (o,i) (i,i) – в слу-
чае (i,o) находит ASCII-код CharParam и передает его IntgParam; в случае
(o,i) находит символ, соответствующий ASCII-коду IntgParam; в случае (i,i) дает истинный результат, если CharParam имеет ASCII-код IntgParam.
str_int(StringParam, IntgParam) (string, integer) (i,o) (o,i) (i,i) – перевод строки StringParam в целое IntgParam.
str_char(StringParam, CharParam) (string, char) (i,o) (o,i) (i,i) – перевод строки StringParam в символ CharParam.
str_real(StringParam, RealParam) (string, real) (i,o) (o,i) (i,i) – пере-
вод строки StringParam в вещественное число RealParam;
upper_lower(StringInUpperCase,StringInLowerCase)(string,string) (i,i) (i,o) (o,i) – переводит прописные буквы в строке StringInUpp erCase в строчные в StringInLowerCase.
upper_lower(CharInUpperCase, CharInLowerCase) (char, char) (i,i) (i,o) (o,i) – преобразует прописные букву CharInUpp erCase в строчную
CharInLowerCase.
Предикаты для работы с динамическими базами данных
consult(DosFil eName) (string) (i) – загружает из файла DosFileN ame
внутреннюю базу данных.
consult(DosFil eName, InternalDatabaseName) (string, InternalDatabaseN ame) (i,i) – загружает из файла DosFileN ame внутреннюю базу дан-
ных с именем InternalDatabaseN ame, объявленную как DATABASE – InternalDatabaseN ame.
save(DosFil eName) (string) (i) – сохраняет внутреннюю базы данных в файле DosFileN ame.
save(DosFil eName, InternalDatabaseName) (string, DatabaseN ame) (i,i) – сохраняет внутреннюю базу данных, объявленную с именем InternalDatabaseN ame, в файле DosFileN ame.
58
ass ert(Term) (InternalDatabaseDomain) (i) – добавляет терм (факт) Term во внутреннюю базу данных.
ass erta(Term) (InternalDatabaseDomain) (i) – добавляет терм (факт) Term во внутреннюю базу данных перед имеющимися фактами.
ass ertz(Term) (InternalDatabaseDomain) (i) – добавляет терм (факт) Term во внутреннюю базу данных после всех имеющихся фактов.
retract(Term) (InternalDatabaseDomain) (i) – удаляет из базы данных
первый факт, который отождествляется с заданным термом Term. retract(Term, InternalDbaseName) (InternalDatabaseDomain, Data-
baseN ame) (i,i) – удаляет из базы данных, объявленной с именем DatabaseN ame, первый факт, который отождествляется с заданным термом Term. retractall(Term,InternalDbaseName)(InternalDatabaseDomain, DatabaseN ame) (i,i) – удаляет из внутренней базы данных с указанным именем
InternalDbaseN ame все термы Term.
retractall(Term) (InternalDatabaseDomain) (i) – удаляет из внутрен-
ней базы данных без имени все термы Term.
Разные предикаты
random(RealVariable) (real) (o) – генерирует случайное действи-
тельное число RealVariablе в пределах от 0 до 1.
random(MaxValue, RandomInt) (integer, integer) (i,o) – генерирует
случайное целое число RandomInt в пределах от 0 до MaxValue. storage(StackSize, HeapSize, TrailSize) (real, real, real) (o,o,o) – опре-
деляет размер имеющейся памяти (в параграфах), где: StackSize – размер стека, Heap Size - размер кучи (динамической памяти), TrailSize – остаток кучи (динамической памяти).
sound(Duration, Frequency) (integer, integer) (i,i) – подает звуковой сигнал длительностью Duration (в сотых долях секунды) и частотой Frequency (в герцах).
beep – подает стандартный звуковой сигнал.
date(Year, Month, Day) (integer, integer, integer) (o,o,o) (i,i,i) – считы-
вает или устанавливает текущую дату.
time(Hours, Minutes, Seconds, Hundredths) (integer, integer, integer, integer) (o,o,o,o) (i,i,i,i) – устанавливает или считывает текущее время.
trace(on/off) (string) (i) (o) – в случае (i) включает trace(on) или вы-
ключает trace(off) трассировку; в случае (o) передает переменной значе-
ние on или off.
59
Предикаты управления
findall(Variable,Atom, ListVariable) (i,i,o) – собирает значения, воз-
никающие в процессе отката (возврата в предыдущее состояние при поиске решения) в список.
not(Atom)(i) – отрицание. Выполняется успешно, если заданный Atom представляет собой цель, которая не достигается, и наоборот.
free(Variable) (i) – выполняется успешно, если переменная Variable свободна.
bound(Variable) (i) – выполняется успешно, если переменной Variable присвоено какое-либо значение.
fail – является причиной невыполнения предиката и, следовательно, возврата в предыдущее состояние.
true – всегда истинный предикат.
getbacktrack(BTOP) (integer) (o) – определяет уровень возвратов при откатах.
cutbacktrack(BTOP) (integer) (i) – устанавливает уровень возвратов при откатах.
60
