views:

181

answers:

2

I'm stuck with recursion, was wondering if anyone can help me out with it.

I have <Receipts> and <Deposits> elements, that are not verbose, i.e. that a <Receipts> element doesn't have an attribute to show what <Deposit> it is towards. I need to figure out <Deposits> "still amount due" and when a last receipt towards it was paid if any.

I'm trying to do it with the following code:

The idea was to take 1st deposit and see if there are receipts. If the deposit isn't fully paid and there are more receipts - call that recorsive function with all the same params except now count in following receipt.

If there aren't any more receipts or deposit is payed - process it correctly (add required attributes). Otherwise proceed with 2nd deposit. And so on.

However, the XSLT crashes with error message that "a processor stack has overflowed - possible cause is infinite template recursion"

I would really appreciate any help/teps... I'm not that great with recursion and can't understand why mine here doesn't work.

Thanks! :)

<!-- 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>
+4  A: 

Your named template classifyDeposits is definitely recursing infinitely. It has no base case. In the <xsl:choose> section you have two conditions: <xsl:when> and <xsl:otherwise>, however you call classifyDeposits in both conditions.

So no matter what you pass to this template, it will call itself. You need to have a base case: a condition in which the template doesn't call itself.

In pseudocode, you're essentially doing this:

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

You need to add another branch, somewhere, that does not call itself.

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

Of course, just having the condition isn't good enough. It needs to be reachable.

Welbog
@Welbog Thank you, seeing it in pseudocode makes it clearer what's going on. I thought that the if statement <xsl:if test="$deposits"> that contains <xsl:choose> statement works as the base case? The branches will keep calling the template with either the same deposit set or excluding first one. But the choose statement will only execute if there are deposits to be processed.Sorry if it's a silly question, just trying to get my head around it.
DashaLuna
The `<xsl:if>` part isn't operating on a parameter you're passing to the template. I'm assuming it's a global variable of some kind. It's definitely not participating in the function's base case, since the value of `$deposits` won't change once the template is called the first time. If it matches the first call, it will match for every subsequent call.
Welbog
@Welbog Oh yes, sorry it's a typo. It should be <xsl:if test="$depositsAll">. It doesn't loop infinitely. Nevertheless it doesn't correctly calculate the DueAmount and I absolutely confused why..:( Should I edit the original message asking for help with the logic or add to the comment?
DashaLuna
Welbog
A: 

One thing i've learned about concerning recursion is that you need to make sure that you have a condition that will trigger when you are done.

example:

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

I don't know xlst at all, but this can be a simple reason why recursion can go into an infinite loop. Consider instead:

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

I hope that helps

Joshua
@Joshua Oh i see your point, I'll have a look at mine again. Thanks
DashaLuna
downvote? anyone care to clarify? and you're welcome Dasha
Joshua