OK...so I promissed an indepth explanation of my recursion problem. First, some keywords for people searching:[ul]
[li]recursion[/li]
[li]recursive[/li]
[li]tutorial[/li]
[li]error[/li]
[li]memory[/li][/ul]
So the original piece of code that I posted is the solution to the problem. To change the code to duplicate the problem change
ItemNum to
Parent in the SQL statement and then change the line:
Code:
rtrn = rtrn & ItemList(indent+1,rs("ItemNum"))
to
Code:
rtrn = rtrn & ItemList(indent+1,rs("Parent"))
In the table
things there is only one row but for explanation purposes lets put three in:
[tt]
ItemNum Parent Name Size
^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^
1 0 Gift Basket 10
2 1 Corn 6
3 1 Jam 4
[/tt]
In this case, when I want to start the whole process of building my HTML tables, I would do an initial call to this function with a 0 indent and 0 parent:
Code:
<table>
<%=ItemList(0,0)%>
</table>
With the code that causes the error, the SQL statement will return one row to the recordset:
[tt]
ItemNum Parent Name Size
^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^
1 0 Gift Basket 10
[/tt]
Because there is one row the loop would be entered,
rtrn will have the appropriate information placed in it and
ItemList will be called to see if "Gift Basket" has any children. This is where the problem starts.
Code:
rtrn = rtrn & ItemList(indent+1,rs("Parent"))
The Current record will be the parent for the next set of items. Because of this, I want to be passing in the current item, but I am passing in the current item's parent:
Code:
rtrn = rtrn & ItemList(1,0)
Compare this to the original call. The parent is identical, so this call to
ItemList will result in the same row being returned. Can you guess what the next call to
ItemList was?
Code:
rtrn = rtrn & ItemList(2,0)
As you can see, the indent is getting deeper, but the records are not going any deeper into the parent/child structure.
The end result of this was code was an out of memory error but the HTML it was generating looked something like this:
Code:
<tr>
<td>Gift Basket</td>
<td>100</td>
</tr>
<tr>
<td> Gift Basket</td>
<td>100</td>
</tr>
<tr>
<td> Gift Basket</td>
<td>100</td>
</tr>
It just kept building the same row, over and over, indenting the Item more and more until the session ran out of memory.
Now let's walk through the correct code.
The initial call to function remains the same, but the first call within the function gets different values. Remember the row that gets returned the first time:
[tt]
ItemNum Parent Name Size
^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^
1 0 Gift Basket 10
[/tt]
This generates a call, within the loop, that looks like this:
Code:
rtrn = rtrn & ItemList(1,1)
Indent = 1
Parent =
1
This means that on the next pass the sql statement will vary slightly and return a different recordset. Only items with a parent of 1:
[tt]
ItemNum Parent Name Size
^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^
2 1 Corn 6
3 1 Jam 4
[/tt]
So we now have two nested instances of the function and the second one contains the above recordset. As we step into the while loop the row for "Corn" is created and then a call to
ItemList is done to get any of "Corn"'s children:
Code:
rtrn = rtrn & ItemList(2,2)
In this third instance of
ItemList the SQL statement is looking for items with a parent=2. There are none, so the recordset is empty, the while loop is never entered and there is no call to
ItemList, nothing is returned and we are back in the second instance.
In the second instance the loop moves to the next record, the HTML row is generated and we check for children of item 3:
Code:
rtrn = rtrn & ItemList(2,3)
This instance of
ItemList (three deep) behaves identically to the "Corn" check and returns nothing.
Back in the second instance, we are out of records in our recordset and the function returns its HTML to the first instance:
Code:
<tr>
<td> Corn</td>
<td>60</td>
</tr>
<tr>
<td> Jam</td>
<td>40</td>
</tr>
Now we are in the first instance and the statment dictates that we append the returned text to the text that was already generated:
Code:
rtrn = rtrn & ItemList(indent+1,rs("Parent"))
Where
rtrn already contains:
Code:
<tr>
<td>Gift Basket</td>
<td>100</td>
</tr>
Giving:
Code:
<tr>
<td>Gift Basket</td>
<td>100</td>
</tr>
<tr>
<td> Corn</td>
<td>60</td>
</tr>
<tr>
<td> Jam</td>
<td>40</td>
</tr>
Since "Gift Basket" is the only item in the recordset we are now at EOF, the loop terminates, the function terminates and returns the completed HTML table. The recursion properly indented each of the rows and didn't crash the system. Hooray for me.[smiley]
A couple of Notes on all of this:[ol]
[li]It is still possible to run out of memory once this function is working properly. If the there are to many children of children of children of children of...you get the idea...then the function will keep instantiating itself until either its done, or it runs out of memory. To prevent this I am limiting users as to how deep the item nesting can go.[/li]
[li]When I was in school I had an instructor tell the class that recursive functions could not contain loops. That is a load of horse .... He insisted on this even after giving a similar function to this one as an example. Of course you can place a recursive call in a loop, as long is there is some methodology for terminating the recursion. No names, but I hope some of his current students read this.[/li][/ol]
If you have any questions about this feel free to ask. I will try to remember to keep the email notification turned on on this one.
---------------------------------------
Where would one begin to study Myology?