【发布时间】:2012-01-31 08:12:06
【问题描述】:
在 XSLT 中从 1 循环到 60 的最佳方法是什么? 我在网上研究,有一些模板可以做到这一点,有没有其他方法,例如内置函数?
【问题讨论】:
标签: xslt
在 XSLT 中从 1 循环到 60 的最佳方法是什么? 我在网上研究,有一些模板可以做到这一点,有没有其他方法,例如内置函数?
【问题讨论】:
标签: xslt
在 XSLT 2.0 中,
<xsl:for-each select="1 to 60">...</xsl:for-each>
但我猜你一定在使用 XSLT 1.0,否则你不会问。
在 XSLT 1.0 中,您应该使用递归:一个模板调用自身,计数器在每次调用时递增,当达到所需值时递归终止。
XSLT 1.0 中也有一个解决方法:只要您的源文档包含至少 60 个节点,您就可以这样做
<xsl:for-each select="(//node())[60 >= position()]">...</xsl:for-each>
【讨论】:
<xsl:variable name="currentCount" select="." /> 但没有成功。
position()
简单递归在处理长序列时的问题是调用堆栈的空间经常变得不足,并且由于堆栈溢出而结束处理。这通常发生在序列长度 >= 1000 时。
避免这种情况的一般技术(可用于任何 XSLT 处理器,即使它不识别尾递归)是 DVC(分而治之)样式递归。
这是一个成功打印从 1 到 1000000 (1M) 的数字的转换示例:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="1000000"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayNumbers">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="not($pStart > $pEnd)">
<xsl:choose>
<xsl:when test="$pStart = $pEnd">
<xsl:value-of select="$pStart"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMid" select=
"floor(($pStart + $pEnd) div 2)"/>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vMid"/>
</xsl:call-template>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$vMid+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
当应用于任何 XML 文档(未使用)时,此转换会产生所需的结果——从 1 到 1000000 的所有数字。
您可以将此转换用于需要“做 N 次”的任何任务。
【讨论】:
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
基于Dimitre Novatchev's 的回答。
示例:
<xsl:variable name="maxItems" select="10" />
<xsl:variable name="sequence" select="any-sequence"/>
<xsl:for-each select="$sequence">
<!-- Maybe sort first -->
<xsl:sort select="@sort-by" order="descending" />
<!-- where the magic happens -->
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
</xsl:for-each>
【讨论】:
V1.0 使用递归的基本示例是这样的:
<xsl:template match="/">
<Root>
<!-- Main Call to MyTemplate -->
<xsl:call-template name="MyTemplate" />
</Root>
</xsl:template>
<xsl:template name="MyTemplate">
<xsl:param name="index" select="1" />
<xsl:param name="maxValue" select="60" />
<MyCodeHere>
<xsl:value-of select="$index"/>
</MyCodeHere>
<!-- < represents "<" for html entities -->
<xsl:if test="$index < $maxValue">
<xsl:call-template name="MyTemplate">
<xsl:with-param name="index" select="$index + 1" />
<xsl:with-param name="total" select="$maxValue" />
</xsl:call-template>
</xsl:if>
</xsl:template>
【讨论】:
XSLT 基于模板工作,您需要一个模板来运行该循环。
您需要构建一个接收开始值和结束值的模板,并在其中使用 start + 1 进行递归调用计算。当 $start 等于 $end 时,您会返回您的模板,而无需再次调用。
在实践中:http://www.ibm.com/developerworks/xml/library/x-tiploop/index.html
【讨论】: