当使用原始递归处理非常长的序列时,可能会发生这种情况。
想象一下sum()
使用递归命名模板实现函数:
<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="sum">
<xsl:with-param name="pSeq" select="/*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="pAccum" select="0"/>
<xsl:param name="pSeq"/>
<xsl:choose>
<xsl:when test="not($pSeq)">
<xsl:value-of select="$pAccum"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sum">
<xsl:with-param name="pAccum"
select="$pAccum+$pSeq[1]"/>
<xsl:with-param name="pSeq"
select="$pSeq[position() >1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于以下 XML 文档时:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
结果是:
55
现在,假设nums
有 1000000 (1M)num
个孩子。这将是寻找一百万个数字的总和的合理尝试,但是大多数 XSLT 处理器通常在递归深度为 1000 或大约 1000 时崩溃。
解决方案:
使用尾递归(一种特殊的递归,其中递归调用是模板中的最后一条指令)。一些 XSLT 处理器识别尾递归并在内部对其进行优化以进行迭代,因此没有递归和堆栈溢出。
使用 DVC 风格的递归(分而治之)。这适用于所有 XSLT 处理器。最大递归深度为 log2(N),对于大多数实际目的是可行的。例如,处理 1M 个项目的序列只需要 19 的堆栈深度。
这是 sum 模板的 DVC 实现:
<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="sum">
<xsl:with-param name="pSeq" select="/*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="pSeq"/>
<xsl:variable name="vCnt" select="count($pSeq)"/>
<xsl:choose>
<xsl:when test="$vCnt = 0">
<xsl:value-of select="0"/>
</xsl:when>
<xsl:when test="$vCnt = 1">
<xsl:value-of select="$pSeq[1]"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select=
"floor($vCnt div 2)"/>
<xsl:variable name="vSum1">
<xsl:call-template name="sum">
<xsl:with-param name="pSeq" select=
"$pSeq[not(position() > $vHalf)]"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vSum2">
<xsl:call-template name="sum">
<xsl:with-param name="pSeq" select=
"$pSeq[position() > $vHalf]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$vSum1+$vSum2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
使用此模板查找一百万个数字的总和需要一些时间,但会产生正确的结果而不会崩溃。