Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы логического программирования в среде Турбо Пролог. Учебное пособие по курсу «Экспертные системы»

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
871 Кб
Скачать

Наиболее распространенным методом сортировки списка является метод вставки. Этот метод основан на неоднократной вставке элементов в список до тех пор, пока список не будет упорядочен. Определим предикат сортировки

sort(S1, S2),

где S1 – исходный (неотсортированный) список, S2 – выходной (отсортированный) список.

Создадим два предиката.

Предикат сортировки sort(S1, S2) организовывает сортировку исходного (неотсортированного) списка S1 в выходной (отсортированный) S2 методом вставки. Если исходный список пуст, то выходной тоже пуст. Пустой список по определению упорядочен.

Выходной список формируется путем вставки головы списка в отсортированный хвост списка так, чтобы итоговый список был упорядоченным. Вставка осуществляется с помощью предиката

insert(X , S1, S2),

где Х – вставляемый элемент, S1 – отсортированный список, в который вставляется элемент Х, S2 – отсортированный список, в котором присутствует элемент Х.

При вставке элемента Х в начало списка S1 проверяется, удовлетворяет ли элемент Х и голова списка условию упорядоченности. Если условие нарушено (в этом случае выполняется предикат cord), то элемент Х добавляется в хвост списка с помощью рекурсивного обращения к предикату insert. Если условие упорядоченности выполняется (предикат cord

не выполняется, так как используется операция отсечения, формируется выходной список S2 путем вставки элемента Х в начало списка S1. В случае если список S1 пустой, выходной список будет состоять из одного вставляемого элемента Х.

Приме р. Произвести сортировку списка целых чисел в порядке возрастания.

domains number=integer list=integer*

p redicates sort(list, list).

cord(number, number). insert(number, list, list).

41

clauses sort([], []).

sort([X| T], S1):– sort(T, S2), insert(X , S2, S1).

insert(X , [Y|S1], [Y|S2]):– cord(X , Y), !, insert(X , S1, S2). insert(X , S1, [X| S1]).

cord(X , Y):– X >Y. goal

sort([15, 20, 3], S).

Результатом будет список S=[3, 5, 20].

Задания для самостояте льной работы

Составить программу в соответствии с вариантом задания. Задан список целых чисел L.

1.Сформировать список L1 из положительных элементов списка L.

2.Сформировать список L1 из отрицательных элементов списка L.

3.Сформировать список L1 из четных чисел списка L.

4.Сформировать список L1 из нечетных чисел списка L.

5.Сформировать список L1 из чисел списка L, кратных k.

6.Определите предикат p olindrom(list). Список называется полидромом, если он читается одинаково, как слева направо, так и справа налево.

Например, [m, a, d, a, m].

7.Сформировать список L1 из элементов: сумма всех n элементов, сумма первых n-1 элементов L и т. д.

8.СформироватьсписокL1 изэлементов:произведениевсехnэлементов списка L, произведение последних n-1 элементов и т. д.

9.Напишите программу, выдающую все возможные комбинации подсписков, которые могут быть сформированы из списка, содержащего 10 элементов.

10.Сформировать список L1 из всех упорядоченных по убыванию подсписков списка L (без дублирования).

42

Глава 3. Создание экспе ртных систем на Прологе

на приме ре игровых программ

3.1.Задача о восьми фе рзях

Взадаче о восьми ферзях требуется разместить N ферзей на квад-

ратной доске N × N так, чтобы на каждой горизонтальной, вертикальной или диагональной линий было не больше одной фигуры. В первоначальной ее формулировке речь шла о размещении 8 ферзей на шахматной доске таким образом, чтобы, согласно правилам игры в шахматы, ни один из ферзей не находится под боем другого. Отсюда пошло название задачи

оферзях.

Эта задача хорошо была изучена в литературе по занимательной математике. Для N =2 и N =3 решения не существует; единственное, без учета симметричного, решение имеет место при N =4. Для N =8 существует 88 (а с учетом симметричных – 92) решений этой задачи.

Один из возможных подходов состоит в создании генератора перестановок,которыйзатемпроверяеткаждуюперестановку,чтобыубедиться, что все ферзи размещены правильно.

Приведем текст программы размещения 8 ферзей на шахматной

доске размером 8 ×8. domains

coord=integer

listcoord=coord*

ferz=coord*

listferz=ferz* p redicates

member(coord, listcoord). notkill(ferz,listferz). solution(listferz).

clauses member(X ,[X| L]).

member(X ,[_|L]):- member(X ,L). notkill(_,[]).

43

notkill([X ,Y],[[X 1,Y1]|Other]):- Y<>Y1, X <>X 1, Y1-Y <> X 1-X , Y1-Y <> X -X 1,

notkill([X ,Y],Other). solution([]).

solution([[X ,Y]|Other]):- solution(Other), member(X ,[1,2,3,4,5,6,7,8]), member(Y,[1,2,3,4,5,6,7,8]), notkill([X ,Y],Other).

goal

S=[F1,F2,F3,F4,F5,F6,F7,F8], solution(S), write(S).

Опишем используемые типы данных:

- coord – одна из координат, определяющих положение какого-

либо ферзя – целое число;

listcoord – используется для представления списка доступных коор-

динат, в данном случае, для шахматной доски 8×8 клеток, список будет представлен набором целых чисел от 1 до 8;

-ferz – список из двух координат, определяющих положение како-

го-либо ферзя;

-listferz – список ферзей (координаты каждого), определяющий

текущее их расположение на шахматной доске.

Опишем используемые в программе предикаты:

member(X ,[LIST]) – успешно согласуется в случае, если X – элемент списка LIST.

notkill(Ferz, ListFerz) – успешен в случае, если ферзь Ferz располо-

жен на доске таким образом, что не попадает под удар ни одного из ферзей из списка ListFerz. Процедура notkill состоит из двух предложений, первое отражает очевидный факт, что если доска пустая (список ферзей пуст), то любая позиция безопасна. Второе предложение сначала проверяет, не попадает ли ферзь с координатами X ,Y под удар первого ферзя

из списка ферзей (координаты X 1,Y1), для этого выполняется проверки на предмет, не стоят ли ферзи на одной горизонтали (Y<>Y1), на одной вертикали (X <>X 1), либо одной из диагоналей (Y1-Y <> X 1-X , Y1-Y <> X -X 1), а затем осуществляет рекурсивный вызов, для проверки безопас-

ности расположения ферзя Ferz по отношению к остальным ферзям из списка ферзей.

solution(listferz) – этот предикат согласуется в случае, если ни один из ферзей из списка listferz не попадает под удар другого – то есть если

расположение ферзей listferz – есть решение поставленной задачи. Про-

44

цедура solution состоит из двух предложений. В пустом списке ферзей ни один ферзь не угрожает другому, о чем и гласит первое предложение процедуры. Второе предложение соответствует утверждению о том, что в заданном списке никто не угрожает никому, если первому ферзю никто не угрожает (notkill([X ,Y],Other)), а оставшиеся ферзи не угрожают друг другу (рекурсивный вызов solution(Other)). Кроме того, во втором предложении введены граничные условия, согласующиеся в том случае, если координаты ферзя не выходят за пределы доски (member(X ,[1,2,3,4,5,6, 7,8]), member(Y,[1,2,3,4,5,6,7,8])).

Цель задает неопределенный список из восьми ферзей (S=[F1, F2,

F3, F4, F5, F6, F7, F8]), затем определяет в нем координаты таким обра-

зом, что бы они удовлетворяли поставленной задаче (solution(S)), а затем

выводит найденное решение на экран (write(S)). Первое найденное решение успешно согласует цель и на этом поиск альтернативных решений прекращается.

3.2. Ханойские башни

Ханойские башни – это игра, в которой используются три штыря и набор дисков. Все диски различаются диаметром и нанизываются на штыри через отверстие в центре каждого диска. Первоначально все диски находятся на левом штыре. Цель игры состоит в том, чтобы переместить все диски на центральный штырь. Правый штырь используется для временного размещения дисков. При каждом перемещении диска с одного штыря на другой должны соблюдаться два ограничения: перемещать можно только самый верхний диск на штыре, и, кроме того, нельзя ставить диск на другой диск меньшего размера.

Стратегия решения данной задачи с применением рекурсии формулируется следующим образом:

-Граничное условие выполняется в случае, когда на исходном (левом) штыре нет дисков.

-Переместить N – 1 дисков с исходного штыря на вспомогательный штырь, используя итоговый штырь как вспомогательный; отметим, что это перемещение осуществляется рекурсивно.

-Переместить один диск с исходного штыря на итоговый штырь. В этом месте наша программа будет выдавать сообщение об этом перемещении.

-Наконец, переместить N – 1 дисков с вспомогательного штыря на итоговый, используя исходный штырь в качестве вспомогательного.

45

Пролог-программа, реализующая данную стратегию, имеет вид:

domains

bi=left; middle; right p redicates

hanoi(integer) move(integer, bi, bi, bi) say(bi,bi)

clauses

hanoi(N ):- move(N , left, middle, right). move(0,_,_,_):-!.

move(N ,A,B,C):- M=N -1, move(M,A,C,B), say(A,B),

move(M,C,B,A).

say(X ,Y):- write («переместили диск со штыря», X ),

write(«на штырь», Y), nl.

Опишем используемые типы данных:

bi (bar identificator – идентификатор штыря) – переменные данно-

го типа могут принимать три фиксированных значения: «left», «middle»,

«right» и служат для обозначения того или иного штыря.

Опишем используемые в программе предикаты:

say(bi,bi) – успешно согласуется всегда, в процессе согласования, используя предикат write, выводит на экран сообщение о том, с какого на какой штырь перемещен диск.

move(integer,bi,bi,bi) – предикат «переместить» имеет четыре аргу-

мента.Первыйаргумент—эточислодисков,которыенужнопереместить. Три другие представляют исходный, итоговый и вспомогательный штыри для перемещения дисков. Процедура move состоит из двух предложений, первое гласит, что если количество перемещаемых дисков равно нулю, то никаких действий производить не надо. Второе предложение осуществляет перемещение дисков в соответствии с вышеописанной стратегией, рекурсивно вызывая саму себя для перемещения N – 1 дисков. В программе не предусмотрено реального хранения и обработки информации о том, какой диск, на каком штыре находится в данный момент времени: вместо перемещения дисков (их цифровых аналогов в памяти компьютера) она лишь выдает на экран последовательность перемещений, которые необходимо выполнить для решения задачи.

hanoi(N ) – означает выдачу сообщений о последовательности пере-

мещений, когда на исходном штыре находится N дисков.

46

3.3. Экспе ртная систем а «Игра в города»

Еще одна игра – «Города» – в полной мере демонстрирует возможности Турбо Пролога как системы, позволяющей создавать самообучающиеся программы. В основу положена распространенная развивающая игра, суть которой заключается в том, что два или более игроков по очереди называют города, причем таким образом, чтобы наименование каждого следующего называемого города начиналось на букву, на которую заканчивается предыдущий. Один и тот же город дважды называть нельзя. Если название города заканчивается на мягкий или твердый знак, то следующий город должен называться на букву, стоящую перед ним (например, Керчь – Челябинск).

Приведенная ниже программа ориентирована на самообучение, поэтому задает вопросы, которые могут показаться лишними. Так, если пользователь ввел название города, неизвестное системе, она просит проверить правильность и подтвердить ввод – это делается для того, чтобы исключить возможные опечатки и не запоминать несколько неправильных названий одного города. Система попросит подтвердить ввод, даже если город введен не по правилам – то есть его первая буква не совпадает с последней у предыдущего города. При этом сначала система запомнит новый для нее город, а уже затем выдаст сообщение о нарушении правил и попросит ввести город в соответствии с правилами. В случае, если система не может назвать подходящий город в ответ на ход пользователя, она сначала признает поражение, затем просит пользователя ввести такой город, который был бы следующим, и только затем завершает игру.

Игра завершается в двух случаях – когда система не может назвать следующий город – поражение компьютера, либо когда пользователь в ответ на предложение сделать следующий ход вводит ключевое слово

«end», что может означать как поражение пользователя, так и нежелание его продолжать игру далее.

Ниже приведен текст программы:

constants upp er_rus=”АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬ Э ЮЯ”

lower_rus=”абвгдеёжзийклмнопрстуфхцчшщъыьэюя” kb=”goroda.p kb”

domains townname=string mode=silent;talk

47

p redicates upp er_lower_rus(char,char)

upp er_lower_rus(char,char,string,string) lastchar(townname,char) nexttown(townname, townname, mode) checktown(townname) getusertown(townname) getnextusertown(townname, townname) userturn(townname) pc turn(townname)

game safeconsult(string) rep eat database-towns town(townname)

database usedtown(townname)

clauses upp er_lower_rus(UC,LC):-upp er_lower_rus(UC,LC, upp er_rus,lower_rus).

upp er_lower_rus(UC,LC,ULS,LLS):-frontchar(ULS,UC,_), frontchar(LLS,LC,_),!. upp er_lower_rus(LC,LC,_,LLS):-frontchar(LLS,LC,_),!. upp er_lower_rus(UC,LC,ULS,LLS):-frontchar(ULS,_,RULS), frontchar(LLS,_,RLLS), upp er_lower_rus(UC,LC,RULS,RLLS). lastchar(TN ,C):-frontchar(TN ,_,R),

R<>””,R<> «ь»,R<> «ъ», lastchar(R,C),!. lastchar(TN ,C):-frontchar(TN ,C,_). nexttown(_,»end»,_):-!. nexttown(TN ,N TN ,_):-lastchar(TN ,CTN ), frontchar(N TN ,CN TN ,_),

upp er_lower_rus(CTN ,C), upp er_lower_rus(CN TN ,C),!.

nexttown(TN ,_,talk):-lastchar(TN ,LC),

write(«\nВы нарушаете правила. Ваш город должен», «начинаться на букву \””,LC, “\”. \n»), fail.

checktown(«end»):-!.

48

checktown(TN ):-usedtown(TN ),!, write(«\nЭтот город уже назывался\n»), fail.

checktown(TN ):-town(TN ),!. checktown(TN ):-TN <>”,

write(«\nЯ не знаю такого города. Проверьте правильность», «написания: «,TN ,» Если все верно, нажмите 1, и я», «запомню его (любая другая клавиша – повторный ввод). \n»),

readchar(C), C=’1’, nl,

assert(town(TN )),!.

checktown(“”):-write(«Вы ничего не ввели - так нельзя!\n»), fail.

checktown( ):-write(“Попробуйте еще раз. \n\n”), fail.

getusertown(TN ):-rep eat, write(«Ваш ход:»),

readln(TN ),

checktown(TN ),!. getnextusertown(TN ,N TN ):-rep eat,

getusertown(N TN ), nexttown(TN , N TN , talk),!.

pc turn(“end”):-!.

pc turn(TN ):-town(N TN ), not(usedtown(N TN )), nexttown(TN ,N TN ,silent),

write( «Мой ход:»,N TN ), nl,!, assert(usedtown(N TN )),

userturn(N TN ). pc turn(TN ):-lastchar(TN ,LC),

write( «\nЯ не могу назвать город на букву \””,LC, “\”. Я сдаюсь. \n А какой бы город назвали вы?»),

getnextusertown(TN , ). userturn(TN ):-getnextusertown(TN ,N TN ),

assert(usedtown(N TN )),pc turn(N TN .)

game:-write(«\nНовая игра\n»),

getusertown(TN ),

assert(usedtown(TN )), pc turn(TN ),

write( «\nИгра окончена\n»). safeconsult(DBname):-existfile(DBname),

49

retractall(_,towns), consult(DBname,towns),!.

safeconsult(_). rep eat. rep eat:-rep eat.

goal safeconsult(kb), rep eat,

retractall(usedtown(_)), game, save(kb,towns),

write(«\nБудете еще играть? (1 - да, любая другая клавиша -

нет). »), readchar(C), C<>’1’.

В самом начале программы определены три константы:

upp er rus и lower rus (заглавные и строчные русские буквы) исполь-

зуются в предикате upp er lower, предназначенном для преобразования

заглавных букв в строчные (встроенный предикат Турбо Пролога делает это некорректно для букв русского алфавита).

kb задает название файла, в котором хранятся знания Пролога о су-

ществующих городах.

Следующий раздел содержит описание типов:

Тип townname предназначен для представления названий городов и эквивалентен строковому типу.

Переменные типа mode могут содержать одно из двух значений – «silent» либо «talk», это значение определяет поведение предиката nexttown

(см. далее).

Далее за перечислением используемых предикатов следует описание двух баз данных – именованная (towns) содержит перечень городов известных системе; безымянная – содержит перечень городов, уже названных за время текущей игры.

Опишем предикаты:

upp er lower rus(char, char), upp er lower rus(char, char, string, string)

предназначены для преобразования заглавных букв в строчные. Первый предикат успешен в случае, когда первый параметр содержит заглавную букву эквивалентную строчной, содержащейся во втором параметре. Для проверки истинности названного условия им вызывается второй предикат, с передачей дополнительно в качестве аргументов двух служебных

50

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]