Списки. Предикаты высших порядков
7.3. Представление множеств списками
Для моделирования множеств часто используются списки. В них хранятся элементы множества (списки не должны содержать повторяющиеся элементы). В следующей программе список разбивается всеми возможными способами на подмножество, состоящее из заданного количества элементов (сочетание), и подмножество оставшихся элементов.
class predicates
subset: (positive, A*, A* Subset [out], A* Rest [out]) nondeterm.
clauses
subset(0, L, [], L):- !.
subset(N, [A | L], [A | L1], L2):-
subset(N - 1, L, L1, L2).
subset(N, [A | L], L1, [A | L2]):-
subset(N, L, L1, L2).
run():-
subset(2, [1, 2, 3, 4, 5], L1, L2),
write(L1, " - ", L2), nl,
fail;
_ = readLine().
Пример
7.1.
Подмножества
Упражнение 1. Сгенерируйте все подмножества элементов списка.
В приведенной ниже программе недетерминированно генерируются перестановки элементов списка. Если список содержит n элементов, то возвращается n! перестановок.
class predicates
permutation: (A*) -> A* nondeterm.
select: (A [out], A*, A* [out]) nondeterm.
clauses
permutation(L) = [A | permutation(L1)]:-
select(A, L, L1).
permutation([]) = [].
select(A, [A | L], L).
select(A, [B | L], [B | L1]):-
select(A, L, L1).
run():-
write(permutation([1, 2, 3])), nl,
fail;
_ = readLine().
Пример
7.2.
Перестановки
Если в списке встречаются одинаковые элементы, то некоторые перестановки, которые выдает программа, приведенная выше, будут одинаковыми. В приведенной ниже программе генерируются попарно различные перестановки элементов списка, который может содержать одинаковые элементы.
class predicates
permutation_rep: (A*) -> A* nondeterm.
select_uniq: (A [out], A*, A* [out]) nondeterm.
clauses
permutation_rep(L) = [A | permutation_rep(L1)]:-
select_uniq(A, L, L1).
permutation_rep([]) = [].
select_uniq(A, [A | L], L).
select_uniq(A, [B | L], [B | L1]):-
select_uniq(A, L, L1),
A <> B.
run():-
write(permutation_rep([1, 2, 3, 2])), nl,
fail;
_ = readLine().
Пример
7.3.
Перестановки списка с повторяющимися элементами
Отличие заключается в определении предиката select. Предикат select_uniq возвращает только попарно различные элементы списка (и список оставшихся элементов).
Для выполнения операций объединения, пересечения и разности множеств предназначены предикаты union, intersection и difference класса list.
В классе list имеются предикаты, которые выполняют аналогичные операции с более сложным определением сравнения элементов. Предикаты
differenceBy: (comparator R, A* L1, A* L2) -> A* differenceEq: (predicate_dt R, A* L1, A* L2) -> A*
удаляют из списка L1 такие элементы X, для которых в списке L2 существует элемент Y, такой, что пара (X, Y) принадлежит отношению R.
Предикаты
intersectionBy: (comparator R, A* L1, A* L2) -> A* intersectionEq: (predicate_dt R, A* L1, A* L2) -> A*
оставляют в списке L1 элементы X, для которых в списке L2 существует элемент Y, такой, что пара (X, Y) принадлежит отношению R.
Предикаты
unionBy: (comparator R, A* L1, A* L2) -> A* unionEq: (predicate_dt R, A* L1, A* L2) -> A*
соединяют список элементов X из L1, для которых не существует элементов Y списка L2, таких, что пара (X, Y) принадлежит отношению R, со списком L2.
open core, console, list
clauses
run():-
L1 = [1, 2, 3, 4, 5], L2 = [4, 2, 5],
write(union(L1, L2)), nl,
write(intersection(L1, L2)), nl,
write(difference(L1, L2)), nl, nl,
write(differenceBy({(X, Y) = compare(X mod 2, Y mod 2)},
L1, L2)), nl,
write(intersectionEq({(X, Y):- X mod 3 = Y mod 3}, L1, L2)),
nl, write(unionEq({(X, Y):- X > Y}, L1, L2)),
_ = readLine().
Пример
7.4.
Операции над множествами
Упражнение 2. Определите результаты применения операций, не запуская программу, приведенную в листинге 7.4.
Предикат compare/2 сравнивает элементы произвольных доменов, но его аргументы должны принадлежать одному и тому же домену. Он возвращает значение equal, greater или less, принадлежащее встроенному домену compareResult. Аргументом продиката differenceBy/3 является процедурный предикат, а аргументом предиката intersectionEq/3 — детерминированный предикат.