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

XSLT 1.0递归超1000次崩溃,如何转为DVC风格?

问题描述

使用XSLT 1.0处理器编写的entry-check递归函数,当index>1000时因栈溢出崩溃,报错信息:

detected a recursion xmlXPathCompiledEval: 3 objects left on stack

其中仅处理cmt节点的递归调用行失效。函数代码如下:

<func:function name="entry-check">
    <xsl:param name="bunch"/>
    <xsl:param name="history"/>
    <xsl:param name="index"/>
    <xsl:param name="depth"/>
    <xsl:variable name="member" select="$bunch[$index]"/>
    <xsl:variable name="vipmember" select="key('member-any',$member/@i)[check-vipmember(.)]"/>
    <xsl:choose>
        <xsl:when test="not($member)">
            <func:result select="$bunch[$history]"/>
        </xsl:when>
        <xsl:when test="$vipmember">
            <func:result select="pp:tern($history,set:trailing($bunch,$member),$vipmember[1])"/>
        </xsl:when>
        <xsl:when test="$member/self::cmt">
            <func:result select="entry-check($bunch,$history,$index -1,$depth)"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:variable name="next" select="pp:version-bunch(key('i',$member[$depth]/@from)/..)"/>
            <func:result select="entry-check($next,$history,count($next),$depth -1)|$bunch[$history]"/>
        </xsl:otherwise>
    </xsl:choose>
</func:function>

参数说明:

  • bunch:节点集
  • history:恒为true()
  • 初始调用时depth=3,index为bunch节点数减1

已尝试改写为尾递归但问题依旧,需要改为DVC(分治)风格或其他有效方案,支持1000-100000次迭代。


解决方案

方案1:节点集过滤替代线性递归遍历cmt

原函数中处理cmt节点的线性递归是栈溢出的核心原因。直接用XPath过滤掉目标范围内的所有cmt节点,一次性定位到需要处理的非cmt节点,彻底消除逐层递归的栈压力。

修改后的函数代码:

<func:function name="entry-check">
    <xsl:param name="bunch"/>
    <xsl:param name="history"/>
    <xsl:param name="index"/>
    <xsl:param name="depth"/>
    
    <!-- 过滤index范围内的所有非cmt节点,取最后一个(对应原逻辑倒序找到的第一个有效节点) -->
    <xsl:variable name="filtered-members" select="$bunch[position() <= $index][not(self::cmt)]"/>
    <xsl:variable name="member" select="$filtered-members[last()]"/>
    
    <xsl:choose>
        <xsl:when test="not($member)">
            <func:result select="$bunch[$history]"/>
        </xsl:when>
        <xsl:when test="key('member-any',$member/@i)[check-vipmember(.)]">
            <func:result select="pp:tern($history,set:trailing($bunch,$member),key('member-any',$member/@i)[check-vipmember(.)][1])"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:variable name="next" select="pp:version-bunch(key('i',$member[$depth]/@from)/..)"/>
            <func:result select="entry-check($next,$history,count($next),$depth -1)|$bunch[$history]"/>
        </xsl:otherwise>
    </xsl:choose>
</func:function>

逻辑说明:

  • 跳过原函数中逐个cmt节点递归的步骤,用XPath一次性筛选出有效节点,避免线性递归的栈累积
  • 保留原函数核心业务逻辑,仅优化遍历cmt节点的方式

方案2:DVC分治实现(适配复杂遍历场景)

如果需要保留更灵活的遍历逻辑,采用分治策略将节点集拆分为前后两部分,递归深度为对数级(即使10万节点,深度仅约17),彻底避免栈溢出。

<!-- 分治核心函数 -->
<func:function name="entry-check-dvc">
    <xsl:param name="bunch"/>
    <xsl:param name="history"/>
    <xsl:param name="depth"/>
    
    <!-- 终止条件:节点集为空 -->
    <xsl:if test="not($bunch)">
        <func:result select="$bunch[$history]"/>
        <xsl:return/>
    </xsl:if>
    
    <!-- 拆分节点集为前后两半 -->
    <xsl:variable name="mid" select="floor(count($bunch) div 2)"/>
    <xsl:variable name="first-half" select="$bunch[position() <= $mid]"/>
    <xsl:variable name="second-half" select="$bunch[position() > $mid]"/>
    
    <!-- 优先处理后半部分(对应原逻辑的倒序遍历) -->
    <xsl:variable name="second-result" select="entry-check-dvc($second-half,$history,$depth)"/>
    
    <!-- 后半部分找到有效结果则直接返回 -->
    <xsl:if test="$second-result[not($second-result = $bunch[$history])]">
        <func:result select="$second-result"/>
        <xsl:return/>
    </xsl:if>
    
    <!-- 否则处理前半部分 -->
    <func:result select="entry-check-dvc($first-half,$history,$depth)|$bunch[$history]"/>
</func:function>

<!-- 适配原函数参数的调用入口 -->
<func:function name="entry-check">
    <xsl:param name="bunch"/>
    <xsl:param name="history"/>
    <xsl:param name="index"/>
    <xsl:param name="depth"/>
    
    <!-- 截取原逻辑中需要处理的节点范围 -->
    <xsl:variable name="target-bunch" select="$bunch[position() <= $index + 1]"/>
    <func:result select="entry-check-dvc($target-bunch,$history,$depth)"/>
</func:function>

逻辑说明:

  • 分治函数优先处理节点集后半部分,匹配原函数倒序遍历的逻辑
  • 递归深度随节点数量呈对数增长,完全规避栈溢出风险
  • 保留原函数的业务判断逻辑,仅改变遍历的递归方式

关键注意事项

  • 因history恒为true(),$bunch[$history]等价于$bunch,可根据实际情况简化代码
  • 确保key('member-any')和check-vipmember()函数的性能,避免节点过多导致的额外性能损耗
  • 测试时需验证过滤/分治后的节点处理顺序与原逻辑一致,保证结果正确性

内容的提问来源于stack exchange,提问作者Ivanhou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 12:05:09