В XSLT 1.0 использование FXSL облегчает решение таких проблем:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="xsl f ext"
>
<xsl:import href="zipWith.xsl"/>
<xsl:output method="text"/>
<xsl:variable name="vMultFun" select="document('')/*/f:mult-func[1]"/>
<xsl:template match="/">
<xsl:call-template name="profitForId"/>
</xsl:template>
<xsl:template name="profitForId">
<xsl:param name="pId" select="1"/>
<xsl:variable name="vrtfProducts">
<xsl:call-template name="zipWith">
<xsl:with-param name="pFun" select="$vMultFun"/>
<xsl:with-param name="pList1" select="/*/*[@repid = $pId]/@amount"/>
<xsl:with-param name="pList2" select="/*/*[@repid = $pId]/@rate"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ext:node-set($vrtfProducts)/*)"/>
</xsl:template>
<f:mult-func/>
<xsl:template match="f:mult-func" mode="f:FXSL">
<xsl:param name="pArg1"/>
<xsl:param name="pArg2"/>
<xsl:value-of select="$pArg1 * $pArg2"/>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к исходно опубликованному исходному XML-документу, получается правильный результат:
310
В XSLT 2.0 то же решение с использованием FXSL 2.0 может быть выражено однострочным XPath:
sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.),
/*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.)
)
)
Вся трансформация:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="f xs"
>
<xsl:import href="../f/func-zipWithDVC.xsl"/>
<xsl:import href="../f/func-Operators.xsl"/>
<!-- To be applied on testFunc-zipWith4.xml -->
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.),
/*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.)
)
)
"/>
</xsl:template>
</xsl:stylesheet>
Опять же, это преобразование дает правильный ответ:
310
Обратите внимание на следующее :
Функция f:zipWith()
принимает в качестве аргументов функцию fun()
(из двух аргументов) и два списка элементов, имеющих одинаковую длину. Он создает новый список такой же длины, элементы которого являются результатом парного применения fun()
к соответствующим k
-ым элементам двух списков.
f:zipWith()
, так как в выражении принимает функцию f:multiply()
и две последовательности соответствующих атрибутов "ammount
" и "rate
". Результатом является последовательность, каждый элемент которой является произведением соответствующих «ammount
» и «rate
».
Наконец, получается сумма этой последовательности.
Нет необходимости писать явную рекурсию, и также гарантируется, что закулисная рекурсия, используемая в f:zipWith()
, никогда не завершится сбоем (для всех практических случаев) с «переполнением стека»