Перейти к содержанию

Операции над множествами

Задача

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

Решение

XPath 1.0

Операция объединения (|) наборов узлов поддерживается и в XPath 1.0, но, чтобы вычислить пересечение или разность, придется проявить изобретательность.

объединение: $set1 | $set2

пересечение: $set1[count(. | $set2) = count($set2)]

разность: $set1[count(. | $set2) != count($set2)]

XPath 2.0

Оператор | остался и в версии XPath 2.0, но добавился еще синоним для него union. Также появились операторы intersect и except, вычисляющие пересечение и разность соответственно.

Объединение: $set1 union $set2

пересечение: $set1 intersect $set2

разность: $set1 except $set2

Обсуждение

В XPath 2.0 наборы узлов заменены последовательностями. Они, в отличие от наборов узлов, упорядочены и могут содержать дубликаты. Однако в случае применения к ним теоретикомножественных операций дубликаты устраняются, а порядок игнорируется, так что последовательности ведут себя как обычные множества. Результат операции никогда не содержит дубликатов, даже если в исходных последовательностях они были.

Оператор except идиоматически используется в XPath 2.0 для отбора всех атрибутов, кроме входящих в заданное множество:

Все атрибуты, кроме @a: @a* except @a

Все атрибуты, кроме @a и @b: @a* except @a, @b

В версии 1.0 решение будет более громоздким:

@*[local-name(.) != 'a' and local-name(.) != 'b']

Интересно, что в XPath теоретикомножественные операции разрешены только над последовательностями узлов, а к атомарным значениям неприменимы. Связано это с тем, что операции выполняются над идентификаторами узлов, а не над значениями. Имитировать операции над значениями в XPath 2.0 можно с помощью показанных ниже выражений. В XPath 1.0 для этого потребуется рекурсия.

объединение: distinct-values( ($items1, $items2) )

пересечение: distinct-values( ($items1[. = $items2] )

разность: distinct-values( ($items1[not(. = $items2)] )