Если в нашем примере со строками требуется получить все номера строк с длиной не более 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 |


