APPENDIX: Recursive Procedure Calls Updated: Jan 22, 2006 Please consider this code based on a user test program: =====ricorre.bas $AppType CONSOLE: $TypeCheck ON declare sub ricorre (dai as integer) sub ricorre defint f if dai < 10 then f = dai + 1 ricorre f print f end if end sub ricorre 0 pause =====ricorre.bas Variable f is "local". The compiler Symbol Table will list it as "ricorref", a concatenation of the procedure name and the source code variable name "f". In HotBasic, variable f is not a temporary stack location but rather a general dimensioned item. As such, code *outside* the procedure could conceivably reference this value of f by using "ricorref". ricorre.bas prints 10, 10, etc; although you might expect it to print 10, 9, ... , 1 instead. What is wrong? Each recurrent call (re-entrance) into the procedure may overwrite f. The solution: =====ricorre1.bas $AppType CONSOLE: $TypeCheck ON declare sub ricorre (dai as integer) sub ricorre defint f push f if dai < 10 then f = dai + 1 ricorre f print f end if pop f end sub ricorre 0 pause =====ricorre1.bas Now the general rule: If a procedure wants to access data after a recursive call (to itself), it saves that data first *before* calling the procedure again recursively which may overwrite it and restores it after the call. Thus, for recursive procedures, usually the first item of business on entry is to save values that might be needed in the procedure code after the recursive call. Typically, the last item of business for such a procedure is to restore the values before its end. Thus, when the procedure resumes after the recursive call, all data needed from this point to the end of the procedure is the same as it was before the call to itself. Therefore, *all* code which might cause an exit from the procedure must GOTO the common end-point where values are restored. Thus, all PUSH statements (or equivalent) are matched with a POP operation. Note that a recursive procedure call might be conditional. In short, recursive procedures are written so they behave the same regardless of whether or not there was a recursive call in an particular call instance. Above we use the stack; but PUSH and POP work only for 2- and 4-byte values like f above. A number of techniques may be used to save and restore other values such as STRING and DOUBLE. A STRING example: Declare SUB PushStr(str1 As STRING) Declare FUNCTION PopStr As STRING 'now we make a "string stack" array and its pointer spSTK DIM strSTK(24) As STRING, spSTK As LONG SUB PushStr(str1 As STRING) INC(spSTK): strSTK(spSTK)=str1 END SUB FUNCTION PopStr RESULT=strSTK(spSTK): DEC(spSTK) END FUNCTION Please note that STRING array strSTK() is our "string stack" designed to hold up to 24 strings of maximum length of 256 bytes each -- the default for As STRING without [*n] syntax specifying an alternate maximum. Also, SUB PushStr does not check for "stack overflow" where stack pointer spSTK might be greater than 24. These factors may be considered when you might create your own STRING stack. For other variable types, such as DOUBLE, the code above may be modified to create PushDbl() and PopDbl procedures. Now that we know how to do it, let us hasten to add that recursive procedure calls are suggested only for simple situations because of limited application stack space (used in the PUSH and POP statements). Application-created stacks, used in PushStr() and PopStr above, may be designed to hold larger numbers of values in order to achieve deeper levels of recursion. Indeed, for more complex situations, *simulation* of recurive calls is better than actually doing recursive calls. Through simulation one may better handle large blocks of data and/or deeper levels of recursion. Example: game situations -- chess, war games, etc, where the "value" of a potential game move needs to be evaluated multiple times in view of an array of potential opponent moves looking ahead "into the future". In such cases, one would simulate recursion rather than actually do it. For deep levels of recursion or when large amounts of data are involved, an entire hard drive may be required to hold the "temporary simulated stack" data. Of course, simulating recursion is natural, when it comes to computing where almost everything is simulated. Unforturnately, PRINT "$1,000,000.00" only simulates one million dollars with pixels on a screen. Alas, I would rather have the real thing! Copyright 2006 James J Keene PhD Original Publication: Jan 21, 2006