Reputation: 610
The xml document below represents 3 number, 2, 2 and 2. A node <s>
is counted as a number and ended with <zero/>
.
<?xml version="1.0" encoding="UTF-8"?>
<nat xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="nat.xsd">
<s>
<s>
<zero/>
</s>
</s>
<s>
<s>
<zero/>
</s>
</s>
<s>
<s>
<zero/>
</s>
</s>
</nat>
I just started learning with xslt and this is one of the exercise for recursion. I could do plus recursively for adding up all numbers but this multiplying more than two number just blows my mind. I have no idea how to do it.
The expected answer for above xml doc is 8s(ignore the format) :
<s><s><s><s><s><s><s><s><zero/></s></s></s></s></s></s></s></s>
My idea was this, I can have a template to do multiplication for two number by adding. So for this 2x2x2, I would do the 2nd 2 times the 3rd 2 that returns 4 and finally do 2*4. But call template don't return value in xslt unlike java or scheme so I appreciate any hints/helps.
Update: I got my answer by adding in a print template to Dimitre's answer. Here it is:
<xsl:template name="print">
<xsl:param name="pAccum"/>
<xsl:choose>
<xsl:when test="$pAccum > 0">
<s>
<xsl:call-template name="print">
<xsl:with-param name="pAccum" select="$pAccum - 1"/>
</xsl:call-template>
</s>
</xsl:when>
<xsl:otherwise>
<zero/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Upvotes: 1
Views: 241
Reputation: 163458
In XSLT 2.0 I would start with a pair of functions:
<xsl:function name="f:toNumber" as="xs:integer">
<xsl:param name="z" as="element(zero)"/>
<xsl:sequence select="count($z/ancestor::*)"/>
</xsl:function>
<xsl:function name="f:fromNumber" as="element()>
<xsl:param name="z" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$z=0"><zero/></xsl:when>
<xsl:otherwise><s><xsl:sequence select="f:fromNumber($z - 1)"/>
</xsl:function>
That tackles the weirdness of your numeric representation.
Now you just need a function that computes the product of a sequence of numbers:
<xsl:function name="f:product" as="xs:integer">
<xsl:param name="in" as="xs:integer"/>
<xsl:sequence select="if (count($in) = 1) then $in[1] else $in * f:product($in[position()>1])"/>
</xsl:function>
and the rest is child's play...
Upvotes: 1
Reputation: 243529
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:call-template name="product">
<xsl:with-param name="pArgs" select="//zero"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="product">
<xsl:param name="pAccum" select="1"/>
<xsl:param name="pArgs" select="/.."/>
<xsl:choose>
<xsl:when test="not($pArgs)">
<xsl:value-of select="$pAccum"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="product">
<xsl:with-param name="pAccum"
select="$pAccum * count($pArgs[1]/ancestor::s)"/>
<xsl:with-param name="pArgs" select="$pArgs[position() > 1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<nat>
<s>
<s>
<zero/>
</s>
</s>
<s>
<s>
<zero/>
</s>
</s>
<s>
<s>
<zero/>
</s>
</s>
</nat>
produces the wanted, correct result:
8
Explanation:
Primitive recursion with stop condition -- empty argument node-set and an accumulator - parameter for passing the currently accumulated result to the next recursive call.
Upvotes: 1