Главная » XSLT » Выполнение теоретико-множественных операций над наборами узлов

0

 

Задача

Требуется найти объединение, пересечение, разность или симметрическую разность двух наборов узлов. Требуется также сравнить два набора узлов на ра­венство и входимость.

Решение XSLT 1.0

Объединение ищется тривиально, так как язык XPath поддерживает эту опе­рацию напрямую:

<xsl:copy-of select="$node-set1 | $node-set2"/>

Для вычисления пересечения двух наборов узлов потребуется более сложное выражение:

<xsl:copy-of select="$node-set1[count(. | $node-set2) = count($node-set2)]"/>

То, что таким образом мы находим все элементы node-set1, которые также принадлежат node-set2, следует из того факта, что объединение node-set2 и некоторого элемента из node-set1, одновременно входящего в node-set2, не приводит к изменению node-set2.

Разность множеств (то есть совокупность элементов, входящих в первое мно­жество и не входящих во второе) вычисляется следующим образом:

<xsl:copy-of select="$node-set1[count(. | $node-set2) != count($node-set2)]"/>

То, что таким образом мы находим все элементы node-set1, которые не принадлежат node-set2, следует из того факта, что объединение node-set2 и некоторого элемента из node-set1, не входящего в node-set2, порождает множество с большим количеством элементов.

Ниже приведен шаблон для вычисления симметрической разности (совокуп­ность элементов, входящих ровно в одно из двух множеств):

<xsl:copy-of select="$node-set1[count(. | $node-set2) != count($node-set2)] | $node-set2[count(. | $node-set1) != count($node-set1)] "/>

Симметрическая разность – это просто объединение двух разностей: между первым и вторым и между вторым и первым множеством. Проверка равенства множеств node-set1 и node-set2:

<xsl:if test="count($ns1|$ns2) = count($ns1) and count($ns1) = count($ns2)">

Два множества равны, если число элементов в их объединении равно сумме числа элементов в каждом множестве по отдельности.

Проверка того, что node-set2 – подмножество node-set1:

<xsl:if test="count($node-set1|$node-set2) = count($node-set1)">

Проверка того, что node-set2 – истинное подмножество node-set1:

<xsl:if test="count($ns1|$ns2) = count($ns1) and count($ns1) > count(ns2)">

XSLT 2.0

В XPath 2.0 операции над множествами встроены. Детали см. в рецепте 1.7.

Обсуждение

Может возникнуть вопрос, что общего между операциями над множества­ми и запросами к XML. Теоретико-множественные операции дают средства для нахождения сходства и различий между множествами элементов документа. А многие базовые запросы, предъявляемые к данным, так или иначе связаны с отысканием сходства и различий.

Рассмотрим, к примеру, такой способ извлечения элементов person из файла people.xml:

<xsl:variable name="males" select="//person[@sex=’m’]"/> <xsl:variable name="females" select="//person[@sex=’f’]"/> <xsl:variable name="smokers" select="//person[@smoker=’yes’]"/> <xsl:variable name="non-smokers" select="//person[@smoker=’no’]"/>

С точки зрения страхования жизни имеет смысл назначать сумму страховки в зависимости от характеристик страхующегося:

<!– Курящие мужчины –> <xsl:variable name="super-risk"

select="$males[count(. | $smokers) = count($smokers)]"/>

<!– Курящие женщины –> <xsl:variable name="high-risk"

select="$females[count(. | $smokers) = count($smokers)]"/> <!– Некурящие мужчины –> <xsl:variable name="moderate-risk"

select="$males[count(. | $non-smokers) = count($non-smokers)]"/> <!– Некурящие женщины –> <xsl:variable name="low-risk"

select="$females[count(. | $non-smokers) = count($non-smokers)]"/>

Возможно, вы заметили, что те же ответы можно получить проще, применив логические операции вместо теоретико-множественных:

<!– Курящие мужчины –> <xsl:variable name="super-risk"

select="//person[@sex=’m’ and @smoker=’y’]"/> <!– Курящие женщины –> <xsl:variable name="high-risk"

select="//person[@sex=’f’ and @smoker=’y’]"/> <!– Некурящие мужчины –> <xsl:variable name="moderate-risk"

select="//person[@sex=’m’ and @smoker=’n’]"/> <!– Некурящие женщины –> <xsl:variable name="low-risk"

select="//person[@sex=’f’ and @smoker=’n’]"/>

А если уже имеются множества мужчин и женщин, то еще эффективнее было бы написать так:

<!– Курящие мужчины –> <xsl:variable name="super-risk"

select="$males[@smoker=’y’]"/> <!– Курящие женщины –> <xsl:variable name="high-risk"

select="$females[@smoker=’y’]"/> <!– Некурящие мужчины –> <xsl:variable name="moderate-risk" select="$males[@smoker=’n’]"/> <!– Некурящие женщины –> <xsl:variable name="low-risk"

select="$females[@smoker=’n’]"/>

Но эти наблюдения не отменяют полезности теоретико-множественного под­хода. Отметим, что операции над множествами работают, ничего не зная о том, что находится в множествах, то есть на более высоком уровне абстракции. Пред­ставьте себе, что имеется сложный XML-документ, и вам интересны следующие четыре множества:

<!– Все элементы, у которых есть дочерние элементы c1 или c2 –> <xsl:variable name="set1" select="//*[c1 or c2]"/>

<!– Все элементы, у которых есть дочерние элементы c3 или c4 –>

<xsl:variable name="set2" select="//*[c3 and c4]"/>

<!– Все элементы, у родителя которых есть атрибут a1–>

<xsl:variable name="set3" select="//*[../@a1]"/>

<!– Все элементы, у родителя которых есть атрибут a2–>

<xsl:variable name="set4" select="//*[../@a2]"/>

В нашем исходном примере было очевидно, что множества мужчин и женщин не пересекаются (как и множества курящих и некурящих). Здесь же такого апри­орного знания нет. Множества могут не пересекаться, полностью совпадать или иметь несколько общих элементов. Есть только два способа выяснить, что общего между множествами setl и set3. Первый – вычислить их пересечение, второй – обойти весь документ еще раз, применяя логическую операцию and к определяю­щим эти множества предикатам. В данном случае очевидно, что пересечение – более правильный путь.

В проекте EXSLT определен модуль set, в который включены функции для выполнения рассмотренных выше операций над множествами. Там применя­ется интересная техника для возвращения результата операции. Вместо того чтобы возвращать результат напрямую, к нему применяется шаблон в режи­ме, соответствующем типу операции. Например, вычислив пересечение, фун­кция set:intersection вызывает для результата команду <xsl:apply- templates mode= " set: intersection"/>. В EXSLT определен шаблон по умолчанию, работающий в этом режиме; он возвращает копию результата в виде фрагмента дерева узлов. Такой способ возврата позволяет переопределить этот шаблон в импортирующей таблице стилей и тем самым подвергнуть результат дальнейшей обработке. Этот прием полезен, но имеет ограничения. Полезен он потому, что позволяет отказаться от функции расширения node-set(), преобра­зующей результат обратно в набор узлов. А ограничен, потому что для каждой операции в импортирующей таблице стилей каждый шаблон можно переопреде­лить не более одного раза. Однако иногда результаты пересечения, выполненного в разных местах одной и той же таблицы стилей, нужно затем обрабатывать совер­шенно по-разному.

Не переживайте, если вам сразу не удалось ухватить суть только что рас­® смотренного приема, применяемого в EXSLT. В главе 16 мы обсудим и этот прием, и другие способы сделать XSLT-код повторно используемым.

См. также

Описание теоретико-множественных операций в EXSLT можно найти на странице http://www.exslt.org/set/index.html.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии