En XSLT 1.0 el uso de FXSL hace que este tipo de problemas fáciles de resolver:
<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>
Cuando se aplica esta transformación en el documento XML de origen originalmente publicado, el resultado correcto se produce:
En XSLT 2.0 la misma solución usando FXSL 2.0 puede ser expresada por un XPath de una sola línea:
sum(f:zipWith(f:multiply(),
/*/*[xs:decimal(@repid) eq 1]/@amount/xs:decimal(.),
/*/*[xs:decimal(@repid) eq 1]/@rate/xs:decimal(.)
)
)
Toda la transformación:
<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>
Una vez más, esta transformación produce la respuesta correcta:
Tenga en cuenta lo siguiente:
La función f:zipWith()
toma como argumentos una función fun()
(de dos argumentos) y dos listas de elementos que tienen la misma longitud. Produce una nueva lista de la misma longitud, cuyos elementos son el resultado de la aplicación por pares de fun()
en los correspondientes k
-ésimo elemento de las dos listas.
f:zipWith()
como en la expresión toma la función f:multiply()
y dos secuencias de correspondiente "ammount
" y "rate
" atributos. El sesult es una secuencia, cada elemento del cual es el producto del correspondiente "ammount
" y "rate
".
Finalmente, se produce el sum de esta secuencia.
No hay necesidad de escribir una recursión explícita y también se garantiza que la recursividad detrás de las escenas se utiliza dentro de f:zipWith()
nunca va a chocar (para todos los casos prácticos) con el "desbordamiento de pila"
No sabía que puede crear nodos completos, asignarlos a una variable y luego consultarlos. Limpio, gracias John, lo probará. – staterium
@ Ravh: No se puede, hasta XSLT 2.0. En 1.0, necesitaría la función de extensión node-set(). – Tomalak