XSLT递归call-template:rec1与rec2执行差异及工作原理问询
嘿,这个问题我太有共鸣了——之前调试XSLT递归模板的时候也被这种差异搞懵过,本质上是两种完全不同的递归执行逻辑在作怪,咱们一步步拆明白:
先看两种典型的模板实现示例
先假设你的两个模板是类似下面的常见写法(毕竟你没贴代码,我就用字符串处理的经典场景举例):
模板 rec1:深度优先的反向拼接递归
<xsl:template name="rec1"> <xsl:param name="input" /> <xsl:if test="$input != ''"> <!-- 先递归处理剩余字符串,再输出当前字符 --> <xsl:call-template name="rec1"> <xsl:with-param name="input" select="substring($input, 2)" /> </xsl:call-template> <xsl:value-of select="substring($input, 1, 1)" /> </xsl:if> </xsl:template>
模板 rec2:带累积参数的尾递归
<xsl:template name="rec2"> <xsl:param name="input" /> <xsl:param name="result" select="''" /> <xsl:choose> <xsl:when test="$input != ''"> <!-- 把当前字符拼接到结果里,传递给下一层递归 --> <xsl:call-template name="rec2"> <xsl:with-param name="input" select="substring($input, 2)" /> <xsl:with-param name="result" select="concat($result, substring($input, 1, 1))" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <!-- 递归终止时返回累积好的完整结果 --> <xsl:value-of select="$result" /> </xsl:otherwise> </xsl:choose> </xsl:template>
核心差异:递归的“求值顺序”和“结果传递方式”
rec1:先递归到底,再一次性输出所有内容
rec1属于深度优先的反向处理,执行逻辑是:
- 每一次调用模板时,都会先触发对剩余字符串的递归调用,直到传入的
$input为空(递归终止条件) - 只有当最底层的递归执行完毕后,才会从递归栈的最底层往回走,每一层在完成子调用后,才输出当前层的第一个字符
- 调试时看到“一次性返回完整字符串”,是因为XSLT会先完成所有嵌套的递归调用,把所有输出内容聚合起来后,才会整体返回结果——你看到的“一次性输出”其实是递归回溯阶段所有输出的拼接结果
举个例子,输入字符串"abc",rec1的执行流程是:
- 调用
rec1("abc")→ 先触发rec1("bc") - 调用
rec1("bc")→ 先触发rec1("c") - 调用
rec1("c")→ 先触发rec1("")(终止,无输出) - 回到
rec1("c"),输出字符"c" - 回到
rec1("bc"),输出字符"b" - 回到
rec1("abc"),输出字符"a"
最终输出"cba",但调试器只会在所有递归完成后,把这些字符作为一个完整字符串展示给你。
rec2:逐层累积结果,每次递归都传递中间值
rec2属于带累积参数的尾递归,执行逻辑是:
- 每一次递归都会把当前处理的字符拼接到
$result参数中,把这个中间结果传递给下一层递归 - 直到
$input为空时,直接返回累积完成的$result - 调试时看到“每次递归调用都会返回一个值”,是因为每一层递归都会接收上一层传递的累积结果,然后直接传递给更上层(直到终止层返回最终结果)——有些调试器会把每一层的返回值都显示出来,所以你能看到结果逐步累积的过程
同样输入"abc",rec2的执行流程是:
- 调用
rec2("abc", "")→ 触发rec2("bc", "a") - 调用
rec2("bc", "a")→ 触发rec2("c", "ab") - 调用
rec2("c", "ab")→ 触发rec2("", "abc") - 调用
rec2("", "abc")→ 返回"abc" - 上一层
rec2("c", "ab")接收这个返回值,再传递给rec2("bc", "a"),最终回到最开始的调用返回"abc"
这里每一层递归都在传递中间结果,所以调试时能看到每一步的返回值都在变化。
为什么最终结果一致?
如果你的两个模板最终输出相同,说明它们的处理逻辑是互补的——比如把rec1里的输出和递归顺序调换(先输出当前字符,再递归),或者让rec2的累积逻辑变成反向拼接,就能得到相同的结果。本质上两者都遍历了字符串的每一个字符,只是结果的构建顺序、传递方式不同而已。
内容的提问来源于stack exchange,提问作者FelHa
相关产品推荐
相关产品推荐

