Операции над списками в ПРОЛОГе
Все они основаны на выделении головы и хвоста и использовании рекурсии, что удобно в связи с неопределенной длиной списка.
Разделение на голову и хвост. Операция осуществляется | (вертикальной чертой )
[Head|Tail]
Пример: вывод элементов списка
Domains.
List=integer*
Predicates
Print_list(list)
Clauses
Print_list([])./*условие прекращения реурсии*/
Print_list([H | T]):- write (H),nl,
Print_list(T).
2-я клауза срабатывает до тех пор, пока Т не станет пустым списком, тогда срабатывает первая(прекращение рекурсии).
Поиск элементов в списке. Очевидно предикат должен иметь 2 аргумента:
1)что искать;
2)список, в котором надо искать.
B= number*
Number=integer
Predicates
Find(H,[H|-]).
Find(H,[-|T]):-
Find(H,T).
1-я клауза срабатывает, когда искомый элемент является 1-й в списке, если нет - производится рекурсивный вызов для списка является хвостом.
Деление списков.
Пусть необходимо разделить список на 2: в одном элементы меньше порога, в
другом - больше.
Идея работы: из исходного списка выделяется голова, включается в 1-й или во 2 -й список до исчерпания исходного списка.
domains
M=integer
List = integer*
predicates
split (M, List, List,List)
clauses
split (M, [M/T], [M/L1], L2: - H<M, split (M, T,
L1,t1))
split (M, [H/T], L1, [H/L2]): - H>M, split (M, T,
L1,L2)
split ( _, [ ], [ ], [ ]. (split
( _, [], _, _).)
split (40, [30, 50, 20, 25, 65, 95], Ls1, Ls2)
Означивание переменных происходит не только при сопоставлении подцелей, но и при сопоставлении заголовка. В данном случае, означивается М как 40, Н - как 30, делается попытка представить первый результирующий список в виде головы и хвоста, но голова при этом уже известна. Это соответствует попытке прилепить голову к неизвестному списку L1. Коль скоро это невозможно (L1 сцеплен с Ls1 и не определен), то действие отклоняется в стек. Проводится условие H<M и выполняется рекурсивный вызов с хвостом списка. Если H<M не согласовывается, все подстановки и означивания отменяются, главный маркер переходит на вторую клаузу. Все повторяется, но голова прилепляется к списку L2. Рекурсивный вызов первой и второй клауз продолжается до тех пор, пока исходный список не станет пустым. В этом случае ни 1-ая, ни 2-ая клауза не могут быть сопоставлены (у пустого списка голову и хвост не выделяют). Срабатывает 3-я клауза. При этом L1 и L2 означиваются как пустые списки. Начинается сворачивание рекурсии, из стека извлекаются очередные значения головы и выполняются отложенные действия: присоединение головы либо к L1, либо к L2. Если результирующие списки не пусты, то в 3-ей клаузе вместо 3-го и 4-го аргументов для успеха сопоставления следует поставить подчерки.
Присоединение списка. Список L1 необходимо присоединить к списку L2, создав список L3, который в начале пустой.
domains
list = integer*
predicates
app (list, list, list,)
clauses
app ([], L, L)
app ([N/L1], L2, [N/L3]): - app (L1, L2, L3)
L1 = [1, 2, 3]
L2 = [4, 5, _]
L3 = [ ]
При срабатывании 1-ой клаузы неозначенная переменная L означивается как L2, затем означивает L3 как L2, и начинается сворачивание рекурсии: к L3 = L2 поочередно добавляются головы 3, 2, 1.
Компоновка данных в список. Для решения этой задачи существует
встроенный предикат с таким предописанием:
findall (<объект входного предиката>,
<входной предикат>,
<имя выходного списка>)
Пример: пусть есть статическая база данных об очках футбольных команд
predicates
football (name, points)
clauses
football (“kom1”, 10).
Football (“kom2”, 7).
........
football (“kom10”, 2).
........
findall (Points, football (-, Points), Points.list)
[10, 7, ..., 2]
Примечание: возможно использование файлов на внешних носителях. Есть такие операции как назначение файла текущим устройством в/в, открытие файла на запись, чтение, дозапись, модификацию и т.д.
Пример: посимвольная запись символов с клавиатуры
clauses
creat_file: -
nl, write (“Введите имя
файла”), nl,
readln (Filename),
openwrite (df,
Filename),
writedevice (df),
readchar (c),
r_w_c (c)
closefile (df).
r_w_c (‘#’):- !. /*конец*/
r_w_c (‘\13’):-
write (‘\13\10’),
readchar (c1)
r_w_c (c1)
.........