【问题标题】:XSLT 1.0 recursionXSLT 1.0 递归
【发布时间】:2010-04-19 15:58:04
【问题描述】:

我被递归困住了,想知道是否有人可以帮助我。

我有<Receipts><Deposits> 元素,它们并不冗长,即<Receipts> 元素没有属性来显示<Deposit> 它的用途。我需要弄清楚<Deposits>“仍需支付的金额”以及最后一张收据何时支付(如果有的话)。

我正在尝试使用以下代码:

我们的想法是收取第一笔存款,看看是否有收据。如果押金没有全额支付并且还有更多收据 - 使用所有相同的参数调用该 recorsive 函数,除了现在计入后续收据。

如果没有更多收据或已支付押金 - 正确处理(添加所需属性)。否则继续进行第二次存款。以此类推。

但是,XSLT 崩溃并显示错误消息“处理器堆栈已溢出 - 可能的原因是无限模板递归”

非常感谢任何帮助/提示...我对递归不是很好,不明白为什么我的这里不起作用。

谢谢! :)

<!-- Accumulate all the deposits with @DueAmount attribute -->
<xsl:variable name="depositsClassified">
    <xsl:call-template name="classifyDeposits">
        <!-- a node-list of all Deposits elements ordered by DueDate Acs -->
        <xsl:with-param name="depositsAll" select="$deposits"/> 
        <xsl:with-param name="depositPrevAmount" select="'0'"/>
        <!-- a node-list of all Receipts elements ordered by ReceivedDate Acs -->
        <xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
        <xsl:with-param name="receiptCount" select="'1'"/>
    </xsl:call-template>
</xsl:variable>


<xsl:template name="classifyDeposits">
    <xsl:param name="depositsAll"/>
    <xsl:param name="depositPrevAmount" select="'0'"/>
    <xsl:param name="receiptsAll"/>
    <xsl:param name="receiptCount"/>


    <xsl:if test="$depositsAll">
        <!-- Do required operations for the 1st deposit -->
        <xsl:variable name="depositFirst" select="$depositsAll[1]"/>
        <xsl:variable name="receiptSum">
            <xsl:choose>
                <xsl:when test="$receiptsAll">
                    <xsl:value-of select="sum($receiptsAll[position() &lt;= $receiptCount]/@ActionAmount)"/>
                </xsl:when>
                <xsl:otherwise>0</xsl:otherwise>
            </xsl:choose>
        </xsl:variable> 
        <xsl:variable name="diff" select="$depositPrevAmount + $depositFirst/@DepositTotalAmount - $receiptSum"/>

        <xsl:choose>
            <xsl:when test="$diff &gt; 0 and
                $receiptCount &lt; $receiptsQuantityOf">
                <xsl:call-template name="classifyDeposits">
                    <xsl:with-param name="depositsAll" select="$depositsAll"/>
                    <xsl:with-param name="depositPrevAmount" select="$depositPrevAmount"/>
                    <xsl:with-param name="receiptsAll" select="$receiptsAll"/>
                    <xsl:with-param name="receiptCount" select="$receiptCount + 1"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- Record changes to the deposit (@DueAmount and receipt ReceivedDate) -->
                <xsl:apply-templates select="$depositFirst" mode="defineDeposit">
                    <xsl:with-param name="diff" select="$diff"/>
                    <xsl:with-param name="latestReceiptForDeposit" select="$receiptsAll[position() = $receiptCount]"/>
                </xsl:apply-templates>


                <!-- Recursive call to the next deposit -->
                <xsl:call-template name="classifyDeposits">
                    <xsl:with-param name="depositsAll" select="$depositsAll[position() &gt; 1]"/>
                    <xsl:with-param name="depositPrevAmount" select="$depositPrevAmount + $depositFirst/@DepositTotalAmount"/>
                    <xsl:with-param name="receiptsAll" select="$receiptsAll"/>
                    <xsl:with-param name="receiptCount" select="'1'"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:if>
</xsl:template>

<!-- Determine deposit's status, due amount and payment received date if any -->
<xsl:template match="Deposits" mode="defineDeposit">
    <xsl:param name="diff"/>
    <xsl:param name="latestReceiptForDeposit"/>

    <xsl:choose>
        <xsl:when test="$diff &lt;= 0">
            <xsl:apply-templates select="." mode="addAttrs">
                <xsl:with-param name="status" select="'paid'"/>
                <xsl:with-param name="dueAmount" select="'0'"/>
                <xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:when test="$diff = ./@DepositTotalAmount">
            <xsl:apply-templates select="." mode="addAttrs">
                <xsl:with-param name="status" select="'due'"/>
                <xsl:with-param name="dueAmount" select="$diff"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:when test="$diff &lt; ./@DepositTotalAmount">
            <xsl:apply-templates select="." mode="addAttrs">
                <xsl:with-param name="status" select="'outstanding'"/>
                <xsl:with-param name="dueAmount" select="$diff"/>
                <xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise/>
    </xsl:choose>
</xsl:template>

<xsl:template match="Deposits" mode="addAttrs">
    <xsl:param name="status"/>
    <xsl:param name="dueAmount"/>
    <xsl:param name="receipt" select="''"/>

    <!-- Constract a new MultiDeposits element with required info -->
    <xsl:copy>
        <xsl:copy-of select="./@*"/>
        <xsl:attribute name="Status"><xsl:value-of select="$status"/></xsl:attribute>
        <xsl:attribute name="DueAmount"><xsl:value-of select="$dueAmount"/></xsl:attribute>
        <xsl:if test="$receipt">
            <xsl:attribute name="latestReceiptDate">
                <xsl:value-of select="$receipt/@ActionDate"/>
            </xsl:attribute>
        </xsl:if>
        <xsl:copy-of select="./*"/>
    </xsl:copy>
</xsl:template>

【问题讨论】:

    标签: xslt recursion xslt-1.0


    【解决方案1】:

    您的命名模板classifyDeposits 肯定是无限递归的。它没有基本情况。在&lt;xsl:choose&gt; 部分,您有两个条件:&lt;xsl:when&gt;&lt;xsl:otherwise&gt;,但是您在这两个条件下都调用了classifyDeposits

    所以无论你向这个模板传递什么,它都会调用自己。您需要有一个基本案例:模板不调用自身的条件。

    在伪代码中,您实际上是在这样做:

    function recursive
      if (A>B) then
        call recursive
      else
        call recursive
    

    您需要在某处添加另一个不会自称的分支。

    function recursive
      if (C=0) then
        return
      else if (A>B) then
        call recursive
      else
        call recursive
    

    当然,仅仅拥有条件还不够好。它必须是可访问的。

    【讨论】:

    • @Welbog 谢谢,在伪代码中看到它可以更清楚地知道发生了什么。我认为包含 语句的 if 语句 可以作为基本情况?分支机构将使用相同的存款集或排除第一个存款集继续调用模板。但是只有在有存款需要处理时才会执行选择语句。对不起,如果这是一个愚蠢的问题,只是想弄清楚它。
    • &lt;xsl:if&gt; 部分未对您传递给模板的参数进行操作。我假设它是某种全局变量。它绝对不参与函数的基本情况,因为一旦第一次调用模板,$deposits 的值就不会改变。如果它匹配第一个调用,它将匹配每个后续调用。
    • 哦,是的,对不起,这是一个错字。它应该是 。它不会无限循环。尽管如此,它并没有正确计算 DueAmount,我完全混淆了为什么..:(我应该编辑请求逻辑帮助的原始消息还是添加到评论中?
    • 你是说你修复了test="$deposits" 部分,现在你不再得到堆栈溢出异常?听起来像您最初所说的问题已解决。如果您还有其他问题,我会修复代码,对您正在尝试做的事情做出更好的解释,如果您仍然感到困惑,请发布另一个问题。通常,向其他人(或者在这种情况下是问答网站)描述问题的行为,你会注意到你之前忽略的一些事情。无论如何,我发现您对您要完成的任务的描述过于模糊,无法理解。
    【解决方案2】:

    我学到的关于递归的一件事是,你需要确保你有一个在你完成后会触发的条件。

    示例:

    decrement_me_until_zero(int i)
    {
         if(i ==0) return;  //we're done here, roll on back up!
         else
         {
             i = i-1;
             decrement_me_until_zero(i);
             return;
         }
    }
    

    我根本不知道 xlst,但这可能是递归可以进入无限循环的一个简单原因。请考虑:

    decrement_me_until_zero(int i)
    {
         if(i ==0) return;  //we're done here, roll on back up!
         else
         {
             decrement_me_until_zero(i);
             i=i-1;                           //oops!  we decrement after we pass the variable down!
             return;
         }
    }
    

    希望对你有帮助

    【讨论】:

    • @Joshua 哦,我明白你的意思了,我会再看看我的。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多