|
Recursive Calls Using Subprocedures
by Kevin Vandever
[The code for this article is available for download.]
In
"Subprocedures: Better than Subroutines," Ted Holt discusses the advantages of
subprocedures over subroutines, and talks about local variables, return values, and the different ways to
define parameters when using subprocedures. One advantage he mentions--but leaves for another
article--is that subprocedures support recursion. Well, this is that article. I'm
going to discuss the basics of recursion and provide an example of when you might want to use it in your
applications.
Why Would I Call Myself?
A recursive call is generated when a subprocedure calls itself. You cannot accomplish this in any other way
using RPG. For instance, you cannot call PROGRAMA from within PROGRAMA. Nor can you call
PROGRAMA, which calls PROGRAMB, which then again calls PROGRAMA. These are no-no's and a
nasty run-time error or, worse, an angry user will tell you so. The same holds true for subroutines. You
cannot call a subroutine from within itself. The good news here is that the compiler won't even let you code
a subroutine that calls itself. Big deal, right? Subprocedures support recursion. So when is the right time to
use recursion? How about if you needed to calculate factorials? You know, 5 factorial (5!) is 5x4x3x2x1, or
120. Computing factorials may be fine for a programming assignment in school, but you are probably
interested in a real-life business reason to employ recursive calls. The best time to take advantage of
recursion is when your data is stored in a hierarchical level as opposed to the relational level to which we
are accustomed. In a relational database, you have a parent-child relationship. That is, you have a master
file in a one-to-many relationship with detail records contained in a detail file. RPG programmers are adept
at displaying or printing information stored in a relational database structure. A hierarchical structure (also
known as a recursive structure) does not have this parent-child relationship. In this hierarchical structure,
the data is independent of the rest of the data while also possibly being associated with it.
The Food Chain
A kit/component structure is a prime example of hierarchical data. A kit, or part, can be the finished
product or a component to another kit. Or it could be both. A company's chain of command is another
example of a hierarchical structure. A person may be a manager or supervisor of other managers and
supervisors. That means a person may be listed in the hierarchy as both manager and subordinate. In using
a relational database system, it would be difficult to list the chain of command. It wouldn't be too bad to
find out who reports to a specific person, but it becomes a bit more complex when you want to find out
who reports to the person who reports to the first person, and so on. If the data is stored in a recursive
fashion, it makes sense that the programmer would employ a recursive method to display it. Let's take a
look at how this concept works.
In my example, I'm going to pass in to my program an employee's ID number and then list the chain of
command using recursive calls to a subfile load subprocedure. I will mark each new level of command by
indenting the names. The following figure contains the output from running my program and shows that Shannon, Ted,
and Jim are direct reports to me. It also shows that Shannon has three direct reports, Tim, Felicia, and
Kalia, while Ted and Jim each have one direct report, Joe and Goofy respectively.
Simple Subfile Stuff
ORGLISTDF
is the DDS that defines a typical load-all subfile. The only interesting thing
to note here is the 60-byte field, SUBNAM, listed in the subfile record format. A field of this size allows
me to concatenate the first and last name of the employee and indent that name to the appropriate level
within the chain of command. By using one long field name, I am able to indent the name a little more to
the right for each level in the chain, without knowing how many levels actually exist. In my example, you
are limited in the number of levels you can display by the number of positions on the screen--you can
indent only so far to the right, correct? However, you could add a field to the subfile to contain one's level
down the food chain and not have to worry about concatenating the names and indenting them, but I chose
to indent the names to further illustrate the effect of recursive calls.
The RPG Mainline
Let's take a look at the RPG program, ORGLIST. I am using
two data files in this example: an employee master file, EMPMST, and a
hierarchical subordinate file, EMPSUB.
The subordinate file will contain two fields per record: the employee
ID and a subordinate ID. I am going to be using one subfile to display the records and will load one level of
command per call to the subprocedure. In my example, there are only two levels under me; therefore, only
two recursive calls to the subprocedure are listed. Refer to Ted's article for a complete
explanation of subprocedures and how they are coded. I am going to concentrate on recursion in this article
and assume you are comfortable with subprocedures. The mainline routine accepts the employee ID from
the user who is calling the program. It then gets the name associated with that ID and formats it for display
in the heading. If the employee ID is not found, I state so in the NAME field and display the screen. If the
ID is found, I am now ready to start building my subfile. First, I execute the CLRSFL routine, which, you
guessed it, clears my subfile. Then, I call the NextLevel subprocedure using the CALLP operation. I pass
the level, which is one the first time through, and the employee ID that was passed in by the user. This one
call to the NextLevel subprocedure will start the recursive process and build the subfile. When control is
returned back to the mainline routine after this call to the NextLevelLevel subprocedure, the subfile is
ready to be displayed. After the call to the subprocedure, I set on my SFLEND indicator, *IN90, check to
see if any records were written to the subfile, and execute the Do Until (DoU) loop to display the subfile.
The Meat
Now the fun begins. Inside my subprocedure, I define a local variable, SAVSUBORD, to hold the current
subordinate ID for the specific call level of the subprocedure. (Yep, this is RPG and I mentioned local
variables without having to size myself for a straitjacket.) Any variables you define inside the subprocedure
are only available within the subprocedure. In my example, I define a variable to save the subordinate
number for return from recursive calls. Oh yeah, any local variables that are defined are local to the specific
running of a subprocedure. Any subsequent calls to the same subprocedure (that would be recursion) will
create new local variables scoped to that instance of the subprocedure. The way the subfile is built is that
each recursive call to a subprocedure will represent a new level in the chain of command. If you take a look
at the structure of my subprocedure, it is built around a Do While (DoW) loop that will find all the
subordinates for the employee ID passed as a parameter to the subprocedure. After the record is found, it is
formatted and written to the subfile. After each successful write to the subfile within the DoW loop,
the NextLevel procedure is called again, passing the current level plus one and the
subordinate's ID that was obtained with the previous read to EMPSUB. This will allow me to find out if the
current subordinate (for example, Shannon) has any subordinate entries in the file before reading the next
record for the current employee (Kevin). Are you with me so far? Only when I return from previous calls to
the NextLevel subprocedure will I set the pointer using the SavSubord field and read the file again for the
current employee. What happened in the meantime is that the NextLevel subprocedure was called as many
times as it was needed to drill down to the lowest level in the chain of command. As each level was
satisfied, the previous level was allowed to finish until you get back to the first instance of NextLevel, in
which case, the whole process starts over again. Take a moment to absorb this information.
One Step at a Time
This may seem a little confusing at first, but if you look at one instance of subprocedure at a time and
realize that it is building the subfile for one level of the org chart, you can then start to understand how each
subsequent call works. One thing to remember is that you can get yourself in trouble if you don't watch
what you're doing. In my example, there is a definite end to my logic; that is, when the last level in the
subordinate file is read. Make sure you understand what you're doing. You don't want to get into the
situation where you have never-ending recursion. That is a bad thing as each recursive call creates another
level in the call stack, so it not only goes on forever, but it sucks up memory in the process. But other than
that, recursion is fun, safe, and a very effective way to process hierarchical data.
|
Sponsored
By
CLIENT SERVER DEVELOPMENT |
|
LEARN SQL FROM
THE SQL EXPERT
Howard F. Arner, Jr.
Buy your copy of iSeries and AS/400 SQL at Work direct from the author, Howard F. Arner, Jr. This is THE book to teach you SQL on the iSeries and AS/400 platform.
Howard takes you from the beginning of the book to the end with information that is useful for beginners and experts alike. The book comes with a CD that has all of the sample data and queries used in the book and a FREE copy of SQLThing that you can use to work through the examples in the book, so you learn by doing.
With Howard's expert guidance, you'll learn and understand many topics, including the following:
- Relational Concepts and Terms
- SQL Select Basics
- Manipulating Data in SQL Tables
- Advanced Select Statements
- Cursors, Transactions, Journals, and Logging
- Debugging SQL Statements and Enhancing Performance
- Stored Procedures
- Embedded SQL in RPG
Howard F. Arner, Jr., is a programmer who has published extensively on integration of AS/400 legacy systems. In his book, iSeries and AS/400 SQL at Work, Howard provides detailed coverage of SQL for business environments by using practical examples--available on the CD--to be performed by the reader. More advanced chapters teach how to become proficient in industry-standard SQL to manipulate AS/400 data, use scalar functions to summarize data, and make legacy RPG and COBOL programs AS/400-compatible. This is the perfect guide for beginners and advanced SQL users.
Here's what readers are saying about iSeries and AS/400 SQL at Work by Howard F. Arner:
Anyone working with AS/400 query should look into buying this book. Now using AS/400 query seems a little odd and very cumbersome. His writing is laid out very well and is easy to read. While reading this, I felt that he was actually there explaining the chapters to me as a one-on-one instructor. -- Jonathan Tripp, Deerfield Beach, Florida
If you wondered how to call AS/400 programs from your Visual Basic program correctly, this book is your one source for how to do so. It's the first book on the market that answered all of my client/server questions when I deal with PC to AS/400 programming tasks.
Not only does Howard do a good job of explaining how stored procedures work, but he gives some great coding examples on how to get you up and running correctly. Howard goes the extra mile and talks about performance, too--one thing that normally is lacking in many other books . -- Bob Butcher, Sidney, New York
Take your knowledge and understanding of SQL to the next level. Order your copy at SQLThing.com.
|
|
|
 |
|