XSLT 1.0 рекурсия - PullRequest
       11

XSLT 1.0 рекурсия

0 голосов
/ 19 апреля 2010

Я застрял с рекурсией, мне было интересно, может ли кто-нибудь помочь мне с этим.

У меня есть элементы <Receipts> и <Deposits>, которые не являются многословными, то есть элемент <Receipts> не имеет атрибута, показывающего, к чему <Deposit> относится. Мне нужно выяснить <Deposits> «все еще причитающуюся сумму» и когда была выплачена последняя квитанция, если таковая имеется.

Я пытаюсь сделать это с помощью следующего кода:

Идея состояла в том, чтобы взять 1-й депозит и посмотреть, есть ли квитанции. Если депозит не оплачен полностью, а квитанций больше - вызовите эту рекурсивную функцию со всеми теми же параметрами, кроме тех, которые теперь учитываются в следующей квитанции.

Если квитанций больше нет или внесен депозит - обработайте его правильно (добавьте необходимые атрибуты). В противном случае перейдите на второй депозит. И так далее.

Однако 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>

Ответы [ 2 ]

4 голосов
/ 19 апреля 2010

Ваш названный шаблон classifyDeposits определенно повторяется бесконечно. У него нет базового варианта. В разделе <xsl:choose> у вас есть два условия: <xsl:when> и <xsl:otherwise>, однако вы звоните 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

Конечно, просто с условием недостаточно. Это должно быть достижимо.

0 голосов
/ 19 апреля 2010

Одна вещь, которую я узнал о рекурсии, это то, что вам нужно убедиться, что у вас есть условие, которое сработает, когда вы закончите.

пример:

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;
     }
}

Надеюсь, это поможет

...