You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用尾递归函数创建无界序列?XSLT栈溢出问题咨询

XSLT生成无界序列栈溢出问题解决方法

问题重现

你用Saxon-EE 11.4写了个递归函数生成全是1的无界整数序列,但调用kooks:repeat()[5]取第5个元素时,直接触发了java.lang.StackOverflowError。你的XSLT代码如下:

<xsl:function name="kooks:repeat" as="xs:integer*">
    <xsl:sequence select="1"/>
    <xsl:sequence select="kooks:repeat()"/>
</xsl:function>

<xsl:template match="/">
    <xsl:sequence select="kooks:repeat()[5]"/>
</xsl:template>

错误提示:

Severity: error
Description: class java.lang.StackOverflowError

对比能正常运行的F#实现:

let rec ones () =
    seq {
        yield 1
        yield! ones ()
    }

let tenMillionthEntry = 
    ones ()
    |> Seq.skip 10000000
    |> Seq.take 1
    |> Seq.head

原因分析

你猜的没错:XSLT函数里两个<xsl:sequence>的拼接操作,让函数没法被识别成尾递归。Saxon处理这种序列拼接时,得保留当前函数的栈帧来完成后续的序列合并,根本做不了尾递归优化。而F#的seq是懒加载序列,yield!会推迟递归调用,只有迭代到对应元素时才触发,不会一次性把栈撑爆。

解决方案

方案1:用xsl:iterate实现无限懒序列

XSLT 3.0的<xsl:iterate>是循环式生成序列,从根源上避免栈溢出问题,不用依赖递归:

<xsl:function name="kooks:repeat" as="xs:integer*">
    <xsl:iterate>
        <!-- 每次迭代输出1 -->
        <xsl:sequence select="1"/>
        <!-- 无限循环下去 -->
        <xsl:next-iteration/>
    </xsl:iterate>
</xsl:function>

<xsl:template match="/">
    <xsl:sequence select="kooks:repeat()[5]"/>
</xsl:template>

<xsl:iterate>是懒加载生成序列的,你取第5个元素时,它只会跑5次迭代,不会累积栈帧。

方案2:调整递归函数为尾递归兼容形式

如果一定要用递归函数,得让函数最后一步直接调用自身,同时合并序列拼接为单个表达式,让Saxon能识别成懒加载:

<xsl:function name="kooks:repeat" as="xs:integer*">
    <xsl:sequence select="1, kooks:repeat()"/>
</xsl:function>

不过这种写法在部分Saxon版本里还是可能栈溢出,毕竟递归拼接序列本质还是靠栈处理调用链,更推荐用第一种xsl:iterate的方案。

内容的提问来源于stack exchange,提问作者MrD at KookerellaLtd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:25:18