Списки. Предикаты высших порядков
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 — детерминированный предикат.