PeopleCode | A summary of the three main ways to perform a loop
A. Introduction
Loops are as integral in PeopleCode as they are in most other programming languages. Performing a set of actions quickly and repeatedly is basically what computerised systems do very well. The loop allows the system to cycle through a block of code until a certain predefined condition is met. In the programming world, an ‘iteration’ describes each pass through the code block. So a loop of ten cycles will perform ten iterations.
Below we take a look at the three main types of loops and how they are used in PeopleCode. These constructs will be familiar to anyone who’s entering PeopleSoft from another programming language.
B. For Loop
This is used for all loops where the number of iterations can be determined in advance of the loop. To set up the loop, you can specify the number of iterations as a fixed number:
For &i = 1 To 10 /* Process each iteration */ End-For;
The ‘for’ loop can also be defined in terms of a variable or a page field:
For &i = &start_num To &end_num /* Process each iteration */ End-For;
In the PeopleSoft world, a common use of the ‘for’ loop is for processing every record in a rowset one-by-one. The ‘ActiveRowCount’ property of the rowset can be used to determine the number of rows:
For &i = 1 To &rowset.ActiveRowCount /* Process each iteration */ End-For;
By default, the ‘for’ loop will increment the loop counter variable by a value of 1 for each iteration. However, this default behaviour can be changed via the ‘Step’ command:
For &i = 1 To 100 Step 10 /* Process each iteration */ End-For;
In this case, the value of &i would be set to 1, 11, 21, 31, 41, etc. The final iteration would see &i set to a value of 91, and then the loop would terminate at the end of this iteration.
‘Step’ can also be used if you wish to process the loop in reverse order:
For &i = &rowset.ActiveRowCount To 1 Step -1 /* Process each iteration */ End-For;
This is typically used when wishing to delete rows in a rowset. By deleting from the bottom-up, the delete of any row will not impact the row number of any preceding row.
To understand why this is necessary, think of the reverse scenario. If you were to delete row number 1 of a multi-row rowset, all the remaining rows would shuffle up by one value. The former row 2 would become the new row 1. However, your ‘for’ loop would happily increment by 1 and then process the new row 2. Row 1 would be skipped entirely. Worse still, the loop was originally coded to run from 1 to ‘&rowset.ActiveRowCount’. As you continually delete from the rowset, the ‘ActiveRowCount’ number is changing all the time, but the loop will still run to whatever the original value of ‘ActiveRowCount’ was set to.
Finally, when coding a ‘for’ loop, it’s quite natural to refer to the loop counter within the loop itself, for instance, in picking row ‘x’ of a multi-row rowset. However, steer clear of any code that could alter the loop counter’s value, even accidentally. While the following piece of code is possible, it will result in a loop with unintended consequences:
For &i = 1 To 10 &i = &i + 1; /* Process each iteration */ End-For;
This problem often comes up when developers copy-and-paste pieces of code from different sources. The developer does not realise that the one variable is being used in different context across the code snippets. This is another good reason why you should never just copy code blindly without having some understanding of what it is doing.
C. While Loop
A ‘while’ loop is typically used when the total number of iterations cannot be determined until run-time. Because the condition of the ‘while’ loop is checked in advance of the code, the loop may not even run at all if the condition is immediately met. If that happened, the code would bypass the entire ‘while… end-while’ block entirely and resume processing after the block.
&finished = False; While not &finished /* Process each iteration */ End-While;
One major danger of the ‘while’ loop is implied by the example above. You may find your code stuck in an ‘infinite loop’ if the loop condition is never met. Using the above example, if the &finished variable never ends up getting set to ‘True’, the loop will run indefinitely (basically until you or a system administrator stop the process).
A typical instance of using a ‘while’ loop in PeopleCode is for uploading records from a flat file. The programme does not know in advance how many lines are about to be read in:
&in_file = GetFile(&file_path, "r", "a", %FilePath_Absolute); While &in_file.ReadLine(&in_line) /* Process current line of file */ end-while;
Similarly, a ‘while’ loop is commonly used when doing a ‘Fetch’ from an SQL select:
&Xlat_SQL = GetSQL(SQL.XX_SELECT_TRANSLATE_VALUES); While &Xlat_SQL.Fetch(&xlat_code, &xlat_descr) /* Process current translate record */ End-While;
D. Repeat Loop
The ‘Repeat’ loop is almost exactly the same as a ‘while’ loop, except that the check to terminate the loop is performed after the loop block, not before it. This means that the ‘Repeat’ loop will always run at least once (unlike a ‘While’, which may not run at all).
&finished = false; Repeat /* Process current record */ Until &finished;
Again like the ‘while’ loop, keep in the mind the danger of being stuck in an infinite loop.
I first learnt to programme using a language called ‘Pascal’. Since repeat loops were a common feature of Pascal, I found myself growing accustomed to ‘repeat’ loops. However for unknown reasons, ‘repeat’ loops are very rarely used in PeopleCode. The ‘while’ loop has become the de-facto standard, even in those situations where a ‘Repeat’ loop would be more appropriate. However, for the sake of completeness (and perhaps for nostalgia), I still wanted to point out that a ‘repeat’ loop is possible within PeopleCode.
E. Taking a Break
This is not an actual loop structure, but as it forms an important part of all three loop types discussed above, it warrants a separate section of its own. At any point during a looping construct, you can exit the loop entirely via the use of the ‘Break’ function. Even if the loop condition has not been met, the ‘Break’ will terminate the code immediately and resume control after the loop.
One common use of ‘Break’ occurs when you are searching a rowset for a particular matching record. As soon as the match is found, you can exit the loop. In other words, the match has been found and there is nothing more to do.
For &i = 1 To &rowset.ActiveRowCount /* Process each iteration, searching for a match */ If &match_found Then Break; End-If; End-For;
If you happen to have a series of nested loops, only the immediate loop will be terminated by the ‘Break’ statement. The ‘parent’ loop will still continue to operate as normal.
For &i = 1 To &rowset.ActiveRowCount For &j = 1 to &rowset.ActiveRowCount /* Process each iteration, searching for a match */ If &match_found Then Break; End-If; End-For; /* If Break fires, code will resume from this point */ End-For;
In this case, the ‘Break’ would terminate the inner loop (variable &j), but the outer loop (variable &i) would continue to run as normal.
See Also:
Tip 038: SQL Selects in PeopleCode