Лучше всегда избегать рекурсии, когда это возможно .
В XSLT 2.0 просто пишется:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumRows" select="7"/>
<xsl:variable name="vDoc" select="/"/>
<xsl:template match="items">
<table>
<xsl:for-each select="1 to $pNumRows">
<tr><xsl:copy-of select="$vDoc/items/item[current()]"/></tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
и когда это преобразование применяется к предоставленному документу XML:
<items>
<item />
<item />
<item />
</items>
желаемый правильный результат получен:
<table>
<tr>
<item/>
</tr>
<tr>
<item/>
</tr>
<tr>
<item/>
</tr>
<tr/>
<tr/>
<tr/>
<tr/>
</table>
Мало кто знает, что для большого числа случаев можно избежать рекурсии и в XSLT 1.0 :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumRows" select="7"/>
<xsl:param name="vDoc" select="/"/>
<xsl:template match="items">
<table>
<xsl:for-each select=
"(document('')//node())[not(position() > $pNumRows)]">
<xsl:variable name="vPos" select="position()"/>
<tr><xsl:copy-of select="$vDoc/items/item[position()=$vPos]"/></tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Это называется методом Пьеза, о котором можно прочитать здесь .
Помните : рекурсия намного медленнее, чем простая итерация - она также имеет тенденцию к аварийному завершению с переполнением стека, если список имеет значительную длину (около 1000 или более) и специальные программные меры не принимаются.