Если в нашем примере со строками требуется получить все номера строк с длиной не более 45, тогда процедура, решающая эту задачу, будет такой:

get_all_DefStrings:-

input_string(NbS, TS, LS),

LS<=45,

write(NbS,’,’),

fail.

get_all_DefStrings. % или get_all_DefStrings:-!.

3.5.2. Использование предиката типа repeat

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

Первую и вторую группы предикатов можно, очевидно, обозначить одним предикатом, p1 и p2 соответственно. Пусть p_control – предикат, проверяющий условие, а предикат repeat (который может, очевидно, иметь любое другое имя) записывается в виде следующих двух правил:

repeat.

repeat:-repeat.

Тогда:

pravilo:-p1,repeat, p2,p_control.

Предикат p1 выполнится один раз, предикат же p2 будет выполняться до тех пор, пока p_control неуспешен.

Разберемся, как это происходит. p1 всегда успешен, затем выполняется всегда успешное первое правило предиката repeat и Пролог устанавливает указатель отката на второе правило. Выполняется p2. После неуспеха p_control выполняется второе правило repeat:-repeat, после чего – первое правило, указатель отката устанавливается на второе правило, далее p2, p_control и т. д.

Приведем простой пример использования repeat. Требуется загрузить факты БД со значениями, вводимыми с клавиатуры. Предикат check_cont запрашивает пользователя о разрешении ввода группы значений факта fact, предикат vvod принимает значения.

НЕ нашли? Не то? Что вы ищете?

database

fact(string, string, string)

predicates

repeat

check_cont

vvod

goal

vvod, exit.

clauses

repeat.

repeat:-repeat.

vvod:-

retractall(fact(_,_,_)),

write(“Начинаем процесс ввода значенний”),nl,

repeat,

clearwindow,

write("A?"),readln(A),nl,

write("B?"),readln(B),nl,

write("C?"),readln(C),nl,

assert(fact(A, B,C)),

check_cont.

check_cont:-

write("ВВОДИТЬ ДАЛЕЕ? (Y|N)"),

readchar(Ans),Ans='N',!,

write("Процесс завершен"),readchar(_).

3.6. Списки в Прологе

3.6.1. Примеры списков

domains

list_integer=integer*

list_struct=el2*

el=e(list_integer)

el2=l(integer, symbol)

predicates

p1(list_integer)

p2(list_struct)

goal

L1=[1,2,3,4]

%Списки списков

L2=[e([10,40,50]),e([30,20])]

....................................

%Списки структур

L3=[l(1,"Fortran"),l(2,"Algol"),l(3,"PL/1")],

write("Языки программирования"),nl,

p2(L3),

search(3,L1),

....................................

3.6.2. Разделение списков на голову и хвост

Основным приемом работы со списками является представление списков в виде «Головы» (Head) и «Хвоста» (Tail). Для иллюстрации напишем правило для предиката p2 из предыдущего раздела:

clauses

p2([]).

p2([l(I, S)|T]):-

write(I," ",S),nl,

p2(T).

Поиск элемента в списке:

search(H,[H,_]).

search(H,[_,T]):-search(H, T).

Получить сумму числовых элементов списка можно очевидным способом:

sumList([],S).

sumList([H|T],S):-S1=S+H, sumList(T, S1).

А для иллюстрации работы со стеком применим «неочевидный» прием:

predicates

sumlist(integer*,integer)

goal

L=[1,2,3,4],

sumlist(L, S),

write(S) .

clauses

sumlist([],0).

sumlist([H|T],X):-sumlist(T, S),X = S+H.

До тех пор, пока не удовлетворено первое правило, Пролог будет очищать список, сбрасывая «головы» в стек. Затем он присвоит 0 свободной переменной. А далее будет суммировать элементы стека, присваявая значения промежуточной суммы переменной X и возвращать элементы стека в список. (По моему мнению, если уж вы выбрали Пролог, не стоит жертвовать логической ясностью ради «оптимизации»)

Встроенный предикат findall

findall собирает компоненты факта в список.

domains

фам_им_от=ф(symbol, symbol, symbol)

лист_фам_им_от=фам_им_от*

facts - f1

гриб(symbol)

язык_программирования(integer, symbol)

фио(фам_им_от)

predicates

собрать_фио(лист_фам_им_от)

................

goal

consult("famio. txt",f1)

%файл "famio. txt" содержит:

%фио("Фадеев","Фиктор","Петрович")

%фио("Иванов","Иван","Иванович")

...............

%фио("Гимазов","Артур","Олегович")

findall(Гриб, гриб(Гриб),Грибы),

findall(Имя_Языка, язык_программирования(_,Имя_Языка),Список),

собрать_фио(СписокФИО).

clauses

собрать_фио(Список):-findall(ФИО, фио(ФИО),Список).

% Список будет состоять из

[ф("Фадеев","Фиктор","Петрович"),ф("Иванов","Иван","Иванович"),...]

3.6.3. Некоторые полезные программы для работы со списками

1. Слияние списков.

Полагаем, что в базе данных facts содержаться два списка (список1, список2), элементами которых являются целые числа. Требуется список1 присоединить к список2. Пусть

список1 есть [5,6,7], а список2 есть [8,9]

domains

список=integer*

facts - f1

список1(список)

список2(список)

predicates

объединить_списки(список, список, список)

goal

список1(Список1),

список2(Список2),!, % А вдруг БД пуста?!

объединить_списки(Список1,Список2,Список3);!.

clauses

объединить_списки([],L, L).

объединить_списки([H|L1],L2,[H|L3]):-

объединить_списки(L1,L2,L3).

Что будет происходить? Поскольку вначале первый список не пуст, Пролог будет пытаться удовлетворить второе правило, очищая первый список. Элементы первого списка пересылаются в стек в оперативной памяти. Когда первый список окажется пустым, становится возможным третьему списку присвоить второй список, и первое правило окажется истинным:

объединить_списки([],[8,9],[8,9]).

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

2. Сортировка списков

goal

sortL([5,4,7,6,11,9],LS)

clauses

sortL([],[]).

sortL([X|T],Sorted_list):-

sortL(T, Sorted_tail),

insert(X, Sorted_tail, Sorted_list).

insert(X,[Y|Sorted_list],[Y|Sorted_list1]):-

ask_order(X, Y),!,

insert(X, Sorted_list, Sorted_list1).

insert(X, Sorted_list,[X|Sorted_list]).

ask_order(X, Y):-X>Y.

3.7. Ввод и вывод

PDC Visual prolog обладает гибкой системой ввода-вывода и манипулирования файлами.

3.7.1. Файловая система

Доступ к файлу может осуществляться в двух «модах» - бинарной и текстовой. Для определения вида доступа используется специальный предикат filemode(SymbolicFileName, Mode).

Параметр Mode принимает одно из двух значений: 0 - Binary Mode, 1 - Text Mode. Дальнейшее изложение имеет отношение только к работе с файлами в текстовой «моде».

Для того, чтобы работать с файлом на внешнем носителе, его нужно открыть или создать. Открыть файл можно для чтения, записи, модификации. Прежде чем это сделать, в файле <имя_проекта>.inc в разделе global domains следует задать символические имена файлов, разделяя их точкой с запятой. (В том месте, где написано %To be edited.). Например, уже написано:

global domains

DB_SELECTOR = browselist_db % For treebrowser tool

FILE = fileselector1; fileselector2 % To be edited

Вы должны добавить Ваши имена:

global domains

DB_SELECTOR = browselist_db % For treebrowser tool

FILE = fileselector1; fileselector2; filein; filein2; fileout % To be edited

Открыть можно практически неограниченное количество файлов, столько, сколько позволяют установки операционной системы (но стоит ли это делать?).

Для перехода от одного открытого файла к другому (перенаправление потоков ввода-вывода) служат предикаты readdevice и writedevice.

Например:

....................

openread(filein,"text. txt")"

openread(filein2,"text2.txt")"

openwrite(fileout,"forwrite. txt")

readdevice(filein),

readln(Str1),

write(Str1)

....................

readdevice(filein2),

readln(Str),

write(Str),

.................

closefile(filein),

closefile(filein2),

closefile(fileout),

.................

Следует помнить, что при открытии файла предикатом openwrite(SimbolicFileName, OSFileName) существующий файл очищается, несуществующий - создается. Для дозаписи существующий файл открывается предикатом

openappend(SimbolicFileName, OSFileName)

Открыть файл для чтения и записи можно предикатом

openmodify(SimbolicFileName, OSFileName).

Кода вы работаете с файлом в режиме чтения - записи, следует после каждой записи использовать предикат

flush(SimbolicFileName),

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

Для реализации нелинейного («гипертекстового») доступа к файлу используется предикат

filepos(SymbolicFileName, FilePosition, Mode),

перемещающий (шаблон (i, i,i)) и берущий (i, o,i) текущую позицию в файле.

Параметр Mode определяет «точку отсчета» позиции:

0 - от начала файла

1 - относительно текущей позиции

2 - относительно конца файла

Предикат eof(SymbolicFileName) предназначен для контроля конца файла.

domains

nondeterm repeat

читать_и_обработать_строки

обработать_строку(symbol)

....................

clauses

repeat.

repeat:-repeat.

читать_и_обработать_строки:-

repeat,

readln(f, S),

обработать_строку(S),

eof(f),!,closefile(f);!.

......................

Файлы можно копировать, переименовывать, удалять, искать.

copyfile(FromName, ToName)

renamefile(OldOSFileName, NewOSFileName)

deletefile(OSFileName)

searchfile(SearchPath, FileName, FoundName)

existfile(OSFileName)

Операции с именами файлов

Для работы с именами файлов используются предикаты:

filenameext(FullName, Name, Mask)-(i, o,o),(o, i,i)

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7