Главная » XSLT » Создание обобщенных функций ограниченного агрегирования

0

Задача

Требуется написать повторно используемые шаблоны для выполнения широ­кого спектра операций ограниченного агрегирования.

Решение

<xsl:template name="generic:bounded-aggregation"> <xsl:param name="x" select="0"/> <xsl:param name="func" select=" ‘identity’ "/> <xsl:param name="func-param1"/>

<xsl:param name="test-func" select=" ‘less-than’ "/> <xsl:param name="test-param1" select="$x + 1"/> <xsl:param name="incr-func" select=" ‘incr’ "/> <xsl:param name="incr-param1" select="1"/> <xsl:param name="i" select="1"/> <xsl:param name="aggr-func" select=" ‘sum’ "/> <xsl:param name="aggr-param1"/> <xsl:param name="accum"

select="$generic:generics[self::generic:aggr-func and

@name = $aggr-func]/@identity"/>

<!– Проверяем, нужно ли продолжать агрегирование –> <xsl:variable name="continue">

<xsl:apply-templates

select="$generic:generics[self::generic:func and

@name = $test-func]"> <xsl:with-param name="x" select="$x"/>

<xsl:with-param name="param1" select="$test-param1"/> </xsl:apply-templates> </xsl:variable>

<xsl:choose>

<xsl:when test="string($continue)"> <!– Вычислить func($x) –> <xsl:variable name="f-of-x"> <xsl:apply-templates select="$generic:generics[self::generic: func

and

@name = $func]">

<xsl:with-param name="x" select="$x"/> <xsl:with-param name="i" select="$i"/>

<xsl:with-param name="param1" select="$func-param1"/> </xsl:apply-templates> </xsl:variable>

<!– Агрегировать текущее значение $f-of-x с $accum –> <xsl:variable name="temp"> <xsl:apply-templates

select="$generic:generics[self::generic:aggr-func and

@name = $aggr-func]"> <xsl:with-param name="x" select="$f-of-x"/> <xsl:with-param name="i" select="$i"/>

<xsl:with-param name="param1" select="$aggr-param1"/> <xsl:with-param name="accum" select="$accum"/> </xsl:apply-templates> </xsl:variable>

<!– Вычислить следующее значение $x–> <xsl:variable name="next-x"> <xsl:apply-templates

select="$generic:generics[self::generic:func and

@name = $incr-func]"> <xsl:with-param name="x" select="$x"/>

<xsl:with-param name="param1" select="$incr-param1"/> </xsl:apply-templates> </xsl:variable>

<!– Рекурсивно обрабатываем остальные узлы, используя position( ) —>

<xsl:call-template name="generic:bounded-aggregation"> <xsl:with-param name="x" select="$next-x"/> <xsl:with-param name="func" select="$func"/> <xsl:with-param name="func-param1" select="$func-param1"/> <xsl:with-param name="test-func" select="$test-func"/> <xsl:with-param name="test-param1" select="$test-param1"/> <xsl:with-param name="incr-func" select="$incr-func"/> <xsl:with-param name="incr-param1" select="$incr-param1"/> <xsl:with-param name="i" select="$i + 1"/> <xsl:with-param name="aggr-func" select="$aggr-func"/> <xsl:with-param name="aggr-param1" select="$aggr-param1"/> <xsl:with-param name="accum" select="$temp"/> </xsl:call-template> </xsl:when> <xsl:otherwise>

<xsl:value-of select="$accum"/> </xsl:otherwise> </xsl:choose>

</xsl:template>

Этот шаблон выполняет агрегирование не по набору узлов, а по множеству значений. Процедура определяется начальным значением, функцией прираще­ния и предикатом, который говорит, когда следует прекратить агрегирование. Функция приращения и функция проверки могут независимо принимать пара­метры. Остальные параметры шаблона generic:bounded-aggregation такие же, как у шаблона generic:aggregation из рецепта 16.2.

Обсуждение

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

Простейший пример применения обобщенной функции ограниченного агре­гирования – это реализация шаблонов factorial и prod-range из рецепта 3.5:

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:generic="http://www.ora.com/XSLTCookbook/namespaces/generic" xmlns:aggr="http://www.ora.com/XSLTCookbook/namespaces/aggregate" xmlns:exslt="http://exslt.org/common" extension-element-prefixes="generic aggr exslt">

<xsl:import href="aggregation.xslt"/>

<xsl:template name="factorial"> <xsl:param name="n" select="0"/>

<xsl:call-template name="generic:bounded-aggregation"> <xsl:with-param name="x" select="$n"/>

<xsl:with-param name="test-func" select=" ‘greater-than’ "/> <xsl:with-param name="test-param1" select="0"/> <xsl:with-param name="incr-func" select=" ‘decr’ "/> <xsl:with-param name="aggr-func" select=" ‘product’ "/> </xsl:call-template>

</xsl:template>

<xsl:template name="prod-range">

<xsl:param name="start" select="1"/> <xsl:param name="end" select="1"/>

<xsl:call-template name="generic:bounded-aggregation"> <xsl:with-param name="x" select="$start"/>

<xsl:with-param name="test-func" select=" ‘less-than-eq’ "/> <xsl:with-param name="test-param1" select="$end"/> <xsl:with-param name="incr-func" select=" ‘incr’ "/> <xsl:with-param name="aggr-func" select=" ‘product’ "/> </xsl:call-template>

</xsl:template>

<xsl:template match="/">

<results>

factorial n="0">

<xsl:call-template name="factorial"/> </factorial>

<factorial n="1">

<xsl:call-template name="factorial">

<xsl:with-param name="n" select="1"/> </xsl:call-template> </factorial>

<factorial n="5">

<xsl:call-template name="factorial">

<xsl:with-param name="n" select="5"/>

</xsl:call-template> </factorial>

factorial n="20">

<xsl:call-template name="factorial">

<xsl:with-param name="n" select="2 0"/> </xsl:call-template> </factorial>

<product start="1" end="20">

<xsl:call-template name="prod-range">

<xsl:with-param name="start" select="1"/> <xsl:with-param name="end" select="2 0"/> </xsl:call-template> </product>

<product start="10" end="20">

<xsl:call-template name="prod-range">

<xsl:with-param name="start" select="10"/> <xsl:with-param name="end" select="2 0"/> </xsl:call-template> </product>

</results>

</xsl:template>

</xsl:stylesheet>

На выходе получаем такой результат:

<results>

factorial n="0">1</factorial> <factorial n="1">1</factorial> <factorial n="5">12 0</factorial>

<factorial n="20">2432902008176640000</factorial> <product start="1" end="20">2432902008176640000</product> <product start="10" end="20">6704425728000</product> </results>

Но это лишь верхушка айсберга! Шаблоном generic:bounded- aggregation можно воспользоваться для численного интегрирования:

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:generic="http://www.ora.com/XSLTCookbook/namespaces/generic"

xmlns:aggr="http://www.ora.com/XSLTCookbook/namespaces/aggregate" xmlns:exslt="http://exslt.org/common" extension-element-prefixes="generic aggr exslt">

<xsl:import href="aggregation.xslt"/>

<xsl:output method="xml" indent="yes"/>

<!– Расширить набор имеющихся обобщенных функций –>

<xsl:variable name="generic:generics" select="$generic:public-generics | document(”)/*/generic:*"/>

<xsl:template name="integrate">

<xsl:param name="from" select="0"/> <xsl:param name="to" select="1"/> <xsl:param name="func" select=" ‘identity’ "/> <xsl:param name="delta" select="($to – $from) div 100"/>

<xsl:call-template name="generic:bounded-aggregation"> <xsl:with-param name="x" select="$from"/> <xsl:with-param name="func" select=" ‘f-of-x-dx’ "/> <xsl:with-param name="func-param1">

<params f-of-x="{$func}" dx="{$delta}"/> </xsl:with-param>

<xsl:with-param name="test-func" select=" ‘less-than’ "/> <xsl:with-param name="test-param1" select="$to"/> <xsl:with-param name="incr-func" select=" ‘incr’ "/> <xsl:with-param name="incr-param1" select="$delta"/> <xsl:with-param name="aggr-func" select=" ‘sum’ "/> </xsl:call-template>

</xsl:template>

<xsl:template name="integrate2">

<xsl:param name="from" select="0"/> <xsl:param name="to" select="1"/> <xsl:param name="func" select=" ‘identity’ "/> <xsl:param name="delta" select="($to – $from) div 100"/>

<xsl:call-template name="generic:bounded-aggregation"> <xsl:with-param name="x" select="$from"/> <xsl:with-param name="func" select=" ‘f-of-x-dx-2′ "/> <xsl:with-param name="func-param1">

<params f-of-x="{$func}" dx="{$delta}"/>

</xsl:with-param>

<xsl:with-param name="test-func" select=" ‘less-than’ "/> <xsl:with-param name="test-param1" select="$to"/> <xsl:with-param name="incr-func" select=" ‘incr’ "/> <xsl:with-param name="incr-param1" select="$delta"/> <xsl:with-param name="aggr-func" select=" ‘sum’ "/> </xsl:call-template>

</xsl:template>

<generic:func name="f-of-x-dx"/>

<xsl:template match="generic:func[@name=’f-of-x-dx’]"> <xsl:param name="x"/> <xsl:param name="param1"/>

<xsl:variable name="f-of-x">

<xsl:apply-templates select="$generic:generics[self::generic:func and @name = exslt:node-set($param1)/*/@f-of-x]">

<xsl:with-param name="x" select="$x"/> </xsl:apply-templates> </xsl:variable>

<xsl:value-of select="$f-of-x * exslt:node-set($param1)/*/@dx"/> </xsl:template>

<generic:func name="f-of-x-dx-2"/>

<xsl:template match="generic:func[@name=’f-of-x-dx-2′]"> <xsl:param name="x"/> <xsl:param name="param1"/>

<xsl:variable name="func" select="exslt:node-set($param1)/*/@f-of-x"/> <xsl:variable name="dx" select="exslt:node-set($param1)/*/@dx"/>

<xsl:variable name="f-of-x"> <xsl:apply-templates

select="$generic:generics[self::generic:func and

@name = $func]"> <xsl:with-param name="x" select="$x"/> </xsl:apply-templates> </xsl:variable>

<xsl:variable name="f-of-x-plus-dx">

<xsl:apply-templates select="$generic:generics[self::generic:func

and @name = $func]">

<xsl:with-param name="x" select="$x + $dx"/> </xsl:apply-templates> </xsl:variable>

<!– Это просто абсолютное значение $f-of-x-plus-dx – $f-of-x –> <xsl:variable name="abs-diff"

select="(1 – 2 *(($f-of-x-plus-dx – $f-of-x) &lt; 0)) * ($f-of-x-plus-dx – $f-of-x)"/>

<xsl:value-of select="$f-of-x * $dx + ($abs-diff * $dx) div 2"/>

</xsl:template>

<xsl:template match="/">

<results>

<integrate desc="intgr x от 0 до 1">

<xsl:call-template name="integrate"/> </integrate>

<integrate desc="intgr x от 0 до 1 с большей точностью"> <xsl:call-template name="integrate">

<xsl:with-param name="delta" select="0.0 01"/> </xsl:call-template> </integrate>

<integrate desc="intgr x от 0 до 1, используя лучший алгоритм">

<xsl:call-template name="integrate2"/> </integrate>

<integrate desc="intgr x**2 от 0 до 1"> <xsl:call-template name="integrate">

<xsl:with-param name="func" select=" ‘square’ "/> </xsl:call-template> </integrate>

<integrate desc="intgr x**2 от 0 до 1, используя лучший алгоритм"> <xsl:call-template name="integrate2">

<xsl:with-param name="func" select=" ‘square’ "/> </xsl:call-template> </integrate>

</results>

</xsl:template> </xsl:stylesheet>

Проблема в том, что пользователь шаблонов интегрирования должен иметь возможность передать любую функцию от x. А нам нужно вычислять сумму, кото­рая является функцией от этой функции. Следовательно, возникает необходи­мость в функции высшего порядка. К тому же этой функции высшего порядка нужно передавать параметр – в данном случае величину delta, используемую для аппроксимации интеграла. Мы достигаем цели, передавая элемент, синтезирован­ный на лету: <params f-of-x="{$func}" dx="{$delta}"/>. Этот составной параметр используется в функциях высшего порядка f-of-x-dx и f-of-x-dx-2. К сожалению, в XSLT 1.0 для извлечения информации из составного параметра приходится обращаться к функции exsl:node-set.

Вот как выглядит результат применения этой таблицы стилей:

<results>

<integrate desc="intgr x от 0 до 1">

0.4950000000000004 </integrate>

<integrate desc="intgr x от 0 до 1 с большей точностью">

0.4995000000000005 </integrate>

<integrate desc="intgr x от 0 до 1, используя лучший алгоритм"> 0.5000000000000001 </integrate>

<integrate desc="intgr x**2 от 0 до 1 с большей точностью"> 0.32835000000000036 </integrate>

<integrate desc="intgr x**2 от 0 до 1, используя лучший алгоритм">

0.33335000000000037 </integrate> </results>

Маловероятно, что вы будете заниматься численным интегрированием с по­мощью XSLT. Не в этом смысл примера. Я лишь хотел продемонстрировать, чего можно достичь с помощью обобщенного повторно используемого кода.

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

По теме:

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