Control structures control the order in which statements will be acted upon by the computer. They can be divided into four basic categories:
Table 4.1 shows structured op-codes that are available in RPG and their equivalent Java statements. Some less-common statements are not shown because they are not available in both languages.
RPG |
Java |
Description |
---|---|---|
IFXX/IF |
if (expression) |
Enables you to execute a code fragment based on a boolean test. |
SELECT |
switch (test) |
Enables you to switch a case (in Java) or select when (in RPG), based ont he value of an expression. |
DO |
for (init; expression; |
Loops a specific number of times based on an initial value, expression, and increment. Loops while the expression is true. |
DOWxx/DOW |
while (expression) |
Loops while the expression is true. |
DOUxx/DOU |
do |
Loops until the expression is false. The body is executed at least once. |
GOTO and TAG |
Java disallows it. RPG allows it, but our advice is do not use it! |
|
LEAVE |
break; |
Transfers control out of the loop. |
ITER |
continue; |
Ends the current iteration of the loop. |
RETURN |
return |
Returns to the caller. |
SETON LR |
System.exit (0) |
|
*PSSR, INFSR, and error indicators |
try/catch |
Catches all program and file exceptions. (See Chapter 10.) |
send escape msg |
throw statement |
Causes an exception to be thrown (See Chapter 10.) |
Let's turn our focus to the RPG op-codes and Java statements that can alter the flow of control, such as if and SELECT/switch.
The IF and ELSE op-codes in RPG, and the if and else statements in Java, enable you to execute different blocks of code based on a simple test. The if/else statement in Java is nearly identical to the if/else statement in C or C++. RPG IV, on the other hand, has two forms of the IF/ELSE op-codes: a fixed form that was inherited from RPG III, and a free-format factor-two form that enables you to write more expressive expressions. The if/else structures in RPG and Java are written differently, but they are the same in function. Listing 4.1 illustrates the two forms of the RPG IV IF/ELSE operations.
Listing 4.1: RPG IV Fixed-Format IF/ELSE and Free-Format IF/ELSE
D* fields D age S 3P 0 INZ(10) D currday S 1B 0 INZ(MON) D price S 3P 2 INZ(0.0) D* constants D MON C CONST(1) D TUE C CONST(2) D WED C CONST(3) D THU C CONST(4) D FRI C CONST(5) D SAT C CONST(6) D SUN C CONST(7) C* Older fixed-format style of IF C age IFGT 64 C currday ANDEQ MON C MOVE 10.00 price C ELSE C MOVE 20.00 price C ENDIF C price DSPLY C* Newer free-format style of IF C IF (age > 64) AND C (currday = MON) C EVAL price = 10.00 C ELSE C EVAL price = 20.00 C ENDIF C price DSPLY C* End the program C EVAL *INLR = *ON
Listing 4.1 performs the same IF/ELSE test twice, once with the classic fixed-format style, and again with the newer free-format style. We think you will agree the new style is easier to read. The details of the new D-specifications for defining fields and constants are discussed in the next chapter, but they should be easy enough to read. Three fields named age, currday, and price are defined, along with seven constants representing days of the week. It then does logic to set the price based on the customer's age and the current day. The first version of the test uses the older fixed-format IF statement. In this format, the IFxx structure is used, where xx can be EQ, GT, LT, GE, LE, and NE. The disadvantage of using the fixed-format is that there is a need for additional op-codes (ANDXX or ORXX) to build each condition.
The second version illustrates the free-format IF operation. In this format, you first specify the IF op-code and then specify the expression that you want to be evaluated in the free-format factor-two entry. The free-format expression consists of the relational operators =, >, <, >=, <=, and , the logical operators AND and OR, and the unary negation operator NOT. The entire expression and sub-expressions can optionally be parenthesized, to aid in readability and to help the compiler parse the sub-expressions. If necessary, the expression can continue on subsequent C-spec lines.
Both the fixed and free versions test if age is greater than 64 and if currday is MON. If both are true, the code executes the statement after the IF op-code and sets price to 10.00; otherwise, execution flows to the ELSE op-code and we execute the statement after it, which sets price to 20.00 if either sub-condition is false. (Expressions and their rules of construction are covered in Chapter 3.). Note that the fixed-format MOVE is used in the former and the free-format EVAL in the latter, to do the assignment of price. In both forms, the IF must eventually match with an ENDIF. You could have multiple statements after the IF or after the ELSE, versus just the one in the example.
The Java if statement, as you might have guessed, is closer to the free-format version of the RPG IF op-code. Listing 4.2 illustrates the same example written in Java.
Listing 4.2: Java if and else Statements
public class TestIf { // constants static final int MON = 1; static final int TUE = 2; static final int WED = 3; static final int THU = 4; static final int FRI = 5; static final int SAT = 6; static final int SUN = 7; public static void main(String args[]) { int age = 65; int currDay = MON; double price = 0.0; if ((age > 64) && (currDay == MON)) price = 10.00; else price = 20.00; System.out.println(price); } }//end of class TestIf
Again, the next chapter covers the details of defining variables and constants in Java, but briefly, constants are coded using the modifiers static and final, and must be at the class level, instead of local to methods. The main method (recall this gets control when you run it from the command line) declares the local variables age, currDay, and price, and follows the same logic as RPG. The variables are of type integer (int) and double-precision floating-point (double). We could have used single-precision float (float), but then we'd have to put an f character after the decimal-point literals to get it to compile; all decimal-point literals in Java are considered double-precision, otherwise. (All of these data types and literals, as well as a better option for decimal-point data, are covered in the next chapter.)
The if/else logic of Listing 4.2 really just does the same test as in RPG. Notice a couple of things, though:
Listing 4.2 is fine, as long as you only have a single line of code for the if body and the else body. If another line is added to either body, you must put the whole body inside braces to identify it as a block. With single lines, the braces are optional; with multiple lines they are required. A problem will result if you add a second line but forget to add the braces, as in:
if ((age > 64) && (currDay == MON)) price = 10.00; discount = 1.00
This will compile, but you will get an unexpected outcome. The second statement will be executed regardless of the outcome of the if test because it is not part of the if body. To prevent this type of error, always use braces, even for single-statement blocks.
Here is the official Java syntax:
if (expression) single-statement; or block of statements else single-statement; or block of statements
The else is optional. Single statements end in semicolon, while blocks start and end with curly braces.
You can see that both languages allow you to specify an arbitrarily complex expression. If the expression evaluates to true, the following block of statements within the structure are executed. If the if expression evaluates to false, then if there is an else statement, the block following it is executed; otherwise, control goes to the subsequent line of code. Java only allows boolean expressions (or boolean variables, as you will discover in Chapter 5) that ultimately evaluate to true or false. If you know C or C++, be aware that Java does not support the idea of allowing numeric expressions and mapping a zero to false and any other number to true. Although Java's syntax is almost identical to C, this is one exception.
An interesting situation arises when you have more than two conditions. Let's take a look at an expanded example. Suppose you would like to have conditions perform one set of instructions if the person is at least 65 years old, a second set if the person is less than 65 but not younger than 12, a third set if the person is less than 12 but not younger than 2, and a default set if the person is 2 or less. That adds up to three different conditions (there could easily be more), plus a default condition that is processed if all three previous conditions are false. Both languages support this kind of testing by allowing you to nest the IF statements. Listing 4.3 illustrates the nesting feature in RPG.
Listing 4.3: Nested IF Statements in RPG
C IF age > 64 C EVAL price = 10.00 C ELSE C IF age > 12 C EVAL price = 20.00 C ELSE C IF age > 2 C EVAL price = 5.00 C ELSE C EVAL price = 0 C ENDIF C ENDIF C ENDIF
You add another IF operation after the ELSE operation, with the new condition you want to test. For more complex conditions, additional ELSE and IF structures can be added. The tricky part is ensuring you eventually have one ENDIF for every IF. By the way, in V5R1 of RPG IV, a very nice enhancement you get is the new op-code ELSEIF, which allows for easy nesting and only a single ENDIF:
C IF age > 64 C EVAL price = 10.00 C ELSEIF age > 12 C EVAL price = 20.00 C ELSEIF age > 2 C EVAL price = 5.00 C ELSE C EVAL price = 0 C ENDIF
Listing 4.4 is this example written in Java. Notice, as is commonly seen in Java, that the else and if statements are lumped together on the same line to aid readability. The rule to always remember, however, is that an else always matches the most previous if. Sound reasonable?
Listing 4.4: Nested if Statements in Java
if (age > 64) price = 10.00; else if (age > 12) price = 20.00; else if (age > 2) price = 5.00; else price = 0;
You can easily get in trouble by using if statements as the body of the if statement itself, like this:
if (age < 65) if (age > 12) price = 20; else price = 10;
It might look as if the else matches the outer if, but it does not. Instead, it matches the inner if. You could fix this by using curly braces, like this:
if (age < 65) { if (age > 12) price = 20; } else price = 10;
Alternatively, you could avoid the situation altogether by writing these if-if statements as single if statements with compound expressions:
if ((age < 65) && (age > 12)) price = 20; else price = 10;
If you use the IF op-code or if statement to code too many tests, you can end up with a deeply nested set of if-else-if conditions. This can make the code clumsy to read and hard to maintain. Both Java and RPG provide you with a more elegant way of doing multiple tests. As an alternative to if-else statements, RPG and Java offer the SELECT op-code and the switch statement, respectively. These give the same results as nested if statements, but they have a simplified structure that is more readable and maintainable. Table 4.2 shows an example using switch and SELECT.
RPG |
Java |
---|---|
SELECT |
switch (currDay) |
{ |
|
WHEN currday = MON |
case MON: |
IF age > 64 |
if (age > 64) |
EVAL price = 10.00 |
price = 20.00; |
ELSE |
else |
EVAL price = 20.00 |
price = 10.00; |
ENDIF |
break; |
WHEN currday = TUE |
case TUE: |
EVAL price = 20.00 |
price = 20.00; break; |
WHEN currday = WED |
case WED: |
EVAL price = 15.00 |
price = 15.00; |
break; |
|
OTHER |
default: |
EVAL price = 25.00 |
price = 25.00; |
ENDSL |
} |
The first important point to note is the improved readability of the SELECT/switch structure compared to the nested if statements. (For brevity, we show the RPG lines starting at the op-code column.) New for SELECT in RPG IV is that the WHEN statements use the free-format factor-two version of the C-spec, allowing you to write free-format expressions just as you do for the new IF statement, in the expanded column range 36 to 80.
You can almost map the structures one-to-one between the languages. The switch statement maps to the SELECT op-code, and the case statement maps to the WHEN op-code. One difference is that Java requires the break to indicate the end of the case statement, while RPG does not need this because the compiler finds the end of the WHEN operation body when it encounters the next WHEN or OTHER operation codes. Otherwise, the default statement in Java maps to the OTHER operation code in RPG, and the final curly brace that ends the switch statement in Java maps to the ENDSL operation code in RPG. Table 4.3 summarizes these mappings.
RPG SELECT |
Java Switch |
---|---|
SELECT |
switch |
WHEN or WHENxx |
case |
OTHER |
default |
ENDSL |
end brace '}' |
Although RPG's SELECT op-code and Java's switch statement seem to be very similar, they differ in the expression that is tested. Here are the details of this important distinction:
There is, in fact, an additional important difference. Did you notice that the RPG SELECT statement in the example was more compact than the Java switch statement? This is primarily because of the Java requirement to end each case statement block with a break. (Note that you can code multiple lines of code before the break without having to use braces as you do for other statement types.) RPG, on the other hand, implicitly defines the end of a WHEN op-code when it sees another one, or an OTHER or ENDSL statement.
This use of break makes Java more verbose than RPG. There are more sinister implications than size of code, however. What do you think will happen if you forget to insert the break statement in Java to terminate the case clause? Rather than implicitly ending the block of code at the next case statement, Java will continue to execute all statements until it finally finds a break statement or the end of the switch statement. This can lead to some nasty bugs that are quite time-consuming to track down. Why does it have this behavior (inherited from C, by the way)? It is actually by design. It allows you to group cases together that have common code. For example, suppose you want to execute one block of code for MON, TUE, and WED, and another one for THUR and FRI. Listing 4.5 shows how you would exploit the break-less behavior for this.
Listing 4.5: Using Breakless Behavior in a Java switch Statement
switch (currDay) { case MON: case TUE: case WED: // first block of code price = 20; break; case THU: case FRI: // second block of code price = 30; break; default: price = 40; } // end switch statement
Chapter 3 introduces the ?: conditional or ternary operator. Under certain circumstances, you can use a shorthand version of the if statement to indicate alternate paths through the code. RPG IV does not support this. The advantages to using this operator are simply reduced typing. However, if you are more comfortable using the if statement, or get paid by the line of code, just use if. The same results will be produced. The following example illustrates an if statement that does a simple test of age < 2. If the test is true, a boolean variable (discussed in Chapter 5) is set to true; otherwise, the variable is set to false:
if (age < 2) freeTicket = true; else freeTicket = false; // ternary equivalent freeTicket = (age < 2) ? true : false;
In some cases, this is a valid alternative to more tedious if statements. (For more details, see Chapter 3.)
The three most common loop structures available in all modern languages, including RPG and Java, are the for loop (DO in RPG), the while loop (DOW in RPG), and the do while loop (DOU in RPG).
Table 4.4 summarizes these different loops. The Java column of Table 4.4 shows the Java constructs for the three types of loops (loop with index, loop while true, loop until false). In all cases, the body is shown inside braces or a block. As with the if statement, however, these braces are optional if the body contains only a single statement.
RPG |
Java |
---|---|
C start DO limit index |
for (initialization; condition; |
C DOW expression |
while (expression) { // body } |
C DOU expression |
do |
The following sections describe all three loops. Notice that the DOW and DOU loops in RPG still have both the fixed-format form that they inherited from RPG III and the free-format factor-two form introduced in RPG IV. Because the free-format form is more structured and easy to code than the fixed-format form, and because we want to encourage you to use the free-format form in your future RPG IV programming, we only use it in the examples that compare RPG loops to Java.
The DO loop in RPG and the for loop in Java are used when a known number of iterations are required to execute a block of statements. This kind of loop structure is called a determinant loop. In this kind of loop, there is a starting and ending value, and an index controls the iteration steps for the loop. Here is the syntax in RPG IV:
C* FACTOR 1 OPCODE FACTOR 2 RESULT C* ———————— —————— ———————— —————— C Start DO End Index C**** loop body C ENDDO
The starting value is specified in factor one and defaults to one if not specified. Factor two contains the ending or limit value, and also defaults to one if not specified. The result column contains the index field. (If not specified, RPG generates an internal field to contain your index.) To delimit the end of your DO loop block, use the ENDDO operation code after the last op-code in the body of the loop. RPG iterates the loop, executing the statements in the body, until the index field reaches the end value. Thus, if the Start field contains a one, and the End field contains a 10, the loop will iterate 10 times. The default increment value is one if you do not specify it. However, you could use factor two of the ENDDO op-code to give RPG a negative or positive increment, or a step value to add or subtract after each loop iteration. Listing 4.6 shows how to use DO.
Listing 4.6: Using DO to Compute Factorial in an RPG IV Procedure
D total S 10U 0 D* Prototype for procedure: Factorial D Factorial PR 10U 0 D number 10U 0 VALUE C EVAL total = Factorial(4) C total DSPLY C EVAL *INLR = *ON P*—————————————————————————————————————————————————————— P* Procedure name: Factorial P* Purpose: Test DO loop by computing a factorial P* Returns: Factorial of given parameter value P* Parameter: number=>number to compute factorial of P*—————————————————————————————————————————————————————— P Factorial B D Factorial PI 10U 0 D number 10U 0 VALUE D* Local fields D index S 5U 0 D total S 10U 0 INZ(1) C* Code to compute factorial C 1 DO number index C EVAL total = total * index C ENDDO C* Return factorial to the caller C RETURN total P Factorial E
This example has a procedure prototype for a procedure named Factorial, mainline C-specs code to call that procedure and display the results, and finally the Factorial procedure itself. This procedure computes the factorial of a given unsigned integer, and returns the result to the caller. The mainline code is tested by passing in the number four, and you can verify yourself the result is 24 (4*3*2*1). The important part is the DO loop inside the Factorial procedure, which loops from one until the given number, and for each value of the index (from one to four), computes the factorial to be the product of the current index times the factorial of the previous index.
The Java for statement contains all the functionality that RPG provides. Listing 4.7 is the same example as Listing 4.6, but written in Java.
Listing 4.7: Using for to Compute Factorial in a Java Method
public class Factorial { public static void main(String args[]) { System.out.println("Welcome to class Factorial"); int total; total = factorial(4); System.out.println(total); } /** Compute and return factorial of given number */ public static int factorial(int inputValue) { int total = 1; for (int index=1; index <= inputValue; index++) total = index * total; return total; } } // end of class Factorial
The static factorial method loops from one to the given number, computing the factorial in the same way as RPG. The difference is the syntax of the for statement, which initializes the index value to one, tells Java to loop while index <= inputValue, and increments the index by one on each iteration of the loop (using index++). Since the for loop's body has only one statement, you do not have to code the braces.
The general format of the Java for statement is as follows:
for (expression) single-statement; or block-of-statements
The expression consists of three parts, separated by semicolons:
for (initialization; condition; increment)
These pieces are typically used as follows:
In Java, unlike RPG, all management of the loop indexing is your responsibility. The only thing Java gives you in terms of built-in support in the for loop is the promise to keep looping as long as the boolean conditioning expression is not false. Absolutely everything else is up to you. So, if you want to have an index variable and increment it per loop iteration, it is your job to take care of that.
Let's look at an example of a typical simple for loop in Java that loops through all elements of an array and prints out the contents. In Java, arrays start at index zero, and they support a special built-in variable named length, which represents the number of items in the array:
char myCharArray[] = new char[20]; // declare an array of 20 characters for (int idx = 0; idx < myCharArray.length; idx++) { myCharArray[idx] = ' '; // set idx'th character to blank }
This first declares an array of 20 characters (arrays are covered in Chapter 6), and then loops through the array, assigning a blank character to each position in it. This example does the following:
The following example runs backward through the array:
char myCharArray[] = new char[20]; // declare an array of 20 characters for (int idx = myCharArray.length-1; idx >= 0; idx—) { myCharArray[idx] = ' '; // set idx'th character to blank }
This example initializes the indexing variable to the size of the array minus one (to account for the zero-based indexing in Java arrays), and decrements the variable by one until it hits zero.
Notice how we declare and initialize the idx variable in one shot. This is handy, but because of Java's scoping rules, it does mean that the variable is not accessible outside the scope of the for loop. It is accessible, however, in all three parts of the for expression and in the body of the loop itself.
Another point about the for loop is that, like all Java multi-statement structures, the braces are optional if the block or body has but one statement. For example, you could write this:
for (int idx = 0; idx < salaries.length; idx++) System.out.println("Salary is: " + salaries[idx]);
Again, however, we caution you that not using braces risks problems when additional statements are added later. (If you forget the braces, then the first statement remains the only one executed per loop iteration.) For the sake of thoroughness, we do show you examples with and without the braces, however.
As it turns out, all three parts of the for statement are optional in Java. For example, you can legally define a for loop with none of the three parts (although the two delimiting semicolons are still required), as in:
for ( ; ; ) System.out.println("looping...");
What does this mean? Because Java agrees to loop as long as the condition expression is not false, this loop will simply run forever. Of course, we humbly recommend not writing infinite-loop programs! What is very interesting about the for loop construct, though, is that it is a completely general construct—that is, the initialization part can contain any statement, the conditional part can contain any boolean expression, and the increment part can contain any statement. It is merely convention, although a convention we recommend you follow, that these are used for index initialization, loop termination checking, and index incrementing or decrementing. The initialization part and the incrementing part actually allow multiple statements to be specified. However, they must be separated by commas, not the usual semicolons. This gives tremendous flexibility.
For a first example of this, let's go back to the for loop, which initialized a character array to all blanks. Because the body of the loop is very small, you actually have the option of performing it in the third part of the for expression, as follows:
for (int idx = 0; idx < myCharArray.length; myCharArray[idx]=' ', idx++) { }
Notice that the assignment statement is put together with the idx++ statement, separated by a comma, in the increment part. This is perfectly legal, and you will see it done often. Because all the necessary work is done in the increment part, there is no need for a body, so an empty block is used. You could also use an empty statement—a simple semicolon. In fact, to be really concise, you could also increment the idx variable right in the assignment statement, as follows:
for (int idx = 0; idx < myCharArray.length; myCharArray[idx++]=' ') ;
You often see this kind of compact for statement in Java code. There is a trick to its interpretation. The expression idx++ uses the current value of idx as the index into the array, and then increments it after. (This is discussed in Chapter 3.) Compact for statements are also commonly used to find the first non-blank character in a character array, like this:
char myCharArray[] = {' ', ' ', 'a', 'b', 'c'}; int idx; for (idx = 0; myCharArray[idx] == ' '; idx++) ; System.out.println("first blank char position = " + idx);
In this example, the index variable idx is declared outside the for loop so it can be accessed later. The for loop then initializes the index to zero, loops as long as the array character indexed by it is blank, and increments the index each time. In this case, the output is 2. Note that myCharArray is declared as an array of characters and initialized to contain the five characters blank, blank, a, b, and c. This implicitly sets its length to five. The number 2 that you get as a result is zero-based; in fact, it is the third position.
Of course, if the input is an array of all blank characters, this example runs into trouble. The terminating condition stays true until the index variable is beyond the limit of the array. This results in a runtime exception, which is not at all unlike an "unmonitored exception" error in RPG:
java.lang.ArrayIndexOutOfBoundsException: 5
To fix this, you need the condition expression to also check for the end of the array:
for (idx = 0; idx < myCharArray.length && myCharArray[idx] == ' '; idx++) ;
Now you have a robust and compact example to check for the first non-blank character. Note that each of the three parts of the for statement is on its own line. Because Java is a free-format language, this is perfectly legal, and is often done for readability. Your subsequent code will want to check if idx is greater than the array's length, which will determine if the array was all blanks or not:
if (idx >= myCharArray.length) System.out.println("All blank array!"); else System.out.println("first blank char position = " + idx);
How would you find the last non-blank character, which is another common programming requirement? You'd code it like this:
for (idx = myCharArrar.length-1; idx >= 0 && myCharArray[idx] == ' '; idx—) ;
In Java, as you will see in Chapter 6, you can have multi-dimensional arrays. Imagine, then, that you have a two-dimensional array named raster, and that you want to initialize it to one for each position. The for loop can handle this easily. It is an array's best friend! Two-dimensional arrays are declared using two sets of empty brackets and the new operator, which specifies the length for each dimension. Note that arrayVariable.length gives the length of the first dimension, and arrayVariable[x].length gives the length of the xth row. Listing 4.8 provides a fully compilable example.
Listing 4.8: Nested for Loops in Java
public class TestFor { public static void main(String args[]) { int raster[][] = new int[3][2]; // matrix 3 by 2 System.out.println(); for (int x=0; x < raster.length; x++) { for (int y=0; y < raster[x].length; y++) { raster[x][y] = x + y; System.out.println("raster " + x + "," + y + " = " + raster[x][y]); } } } } // end TestFor class
The example in Listing 4.8 results in the following output:
raster 0,0 = 0 raster 0,1 = 1 raster 1,0 = 1 raster 1,1 = 2 raster 2,0 = 2 raster 2,1 = 3
This illustrates the ability to nest for loops. That is, the body of one for loop can be yet another for loop. This means the inner loop is executed for its duration multiple times, once per iteration of the outer loop. This example is a little bit longer than necessary. Here is the smallest possible version of this nested for loop for initializing a two-dimensional array (without the println statement):
for (int x=0; x < raster.length; x++) for (int y=0; y < raster[x].length; raster[x][y] = x + y++) ;
As of V4R4 of RPG IV, you have a free-format alternative to the fixed-format DO loop. There is a new op-code named FOR that makes it very easy to write a loop that increments a given index field from a given start value to a given end value. Listing 4.9 is the Factorial procedure originally from Listing 4.6, with the DO loop replaced by a FOR loop.
Listing 4.9: A Free-Format FOR Loop in RPG
P Factorial B D Factorial PI 10U 0 D number 10U 0 VALUE D* Local fields D index S 5U 0 D total S 10U 0 INZ(1) C* Code to compute factorial C FOR index = 1 C BY 1 C TO number C EVAL total = total * index C ENDFOR C* Return factorial to the caller C RETURN total P Factorial E
The FOR op-code uses a free-format version of the C-spec that ranges from column 36 to column 80, as do the other free-format factor-two op-codes: CALLP, DOU, DOW, EVAL, EVALR, IF, RETURN, and WHEN. Not only does this give you more room for writing free-format expressions, it also allows you to easily continue that expression in the same column range of subsequent continued C-spec lines.
Here is the official syntax for the RPG IV FOR loop op-code:
OPCODE EXTENDED-FACTOR 2 —————— ———————————————-——————————————— FOR index-name < = starting-value > < BY increment-value > < TO | DOWNTO limit-value > ENDFOR | END
The entries inside angle brackets are optional. The index field must be declared before the loop, on a D-spec, and must be numeric with zero decimal positions. The starting, increment, and limit values can be a numeric literal or a numeric expression, including a built-in function call or a procedure call. The increment value is always positive. To decrement, specify DOWNTO instead of TO, which causes the increment-value to be subtracted from the index field. Note that all three parts of the FOR op-code can be placed on one line or multiple lines. RPG exits the body of the loop when the index field's value reaches the limit value. Unlike Java, the index field is automatically incremented or decremented by the runtime.
Like the DO loop, to exit the loop prematurely, you have to either set the index field value to be the limit value, or use the LEAVE op-code. Unlike Java, there is no way to test an arbitrary expression to determine when to exit. However, this is okay because typically you'll use the DOW or DOU op-codes for loops that need to do this.
We think you have to agree this FOR loop is easy to code and easy to read, and it certainly is a closer match to Java than the older fixed-format DO loop. See the RPG IV reference manual for more examples of the FOR loop. (Here's another way to find more information: in the CODE/400 editor, position the cursor on the FOR op-code and press F1.)
Now that you have mastered the for loop, you will find the while loop in Java and DOW loop in RPG IV to be a breeze. It is a subset of the for loop's functionality and brings nothing new to the table—except ease of use. The while statement is simpler than the for statement because the while loop only takes an expression for evaluation and does not have as many parameters. Its use is for those cases where the termination of the loop is not predetermined and you simply want to loop while a given condition is true. You will decide in the body of the code when to set that condition to false. (If this procedure is not performed correctly, you will have an infinite loop.) This is called an indeterminant loop because you can't predict when it will end. It is especially useful when reading or asking for input, since the end will be determined by the end of file marker or by a user-initiated action, for example.
The while loop for both Java and RPG executes zero or more times. This point becomes important in contrast to the next section, describing the do until loop, which executes one or more times.
In the free-format DOW operation code in RPG, you specify an expression in the free-format factor two to be evaluated each time the loop iterates. If the expression evaluates to true, the next iteration is executed. However, if the condition or the expression specified in factor two is false, the loop is terminated and control is transferred to the operation after the ENDDO op-code. Here is the syntax:
C* FACTOR 1 OPCODE EXTENDED-FACTOR 2 C* ———————— —————— ———————————————— C DOW Expression C**** loop body C ENDDO
To show this op-code in use, imagine your child asks you for a loan, and you want to give them a printout of the payment schedule. Because you are a softee for your kids, you don't charge them any interest. (We know—you have no "interest" in such a loan, but it is the "principal" that is important here!) Listing 4.10 shows an RPG IV procedure to calculate and print out the amount paid and amount owing after each payment. The procedure takes as input the total amount of the loan and the amount to pay each payment and returns the total number of payments.
Listing 4.10: A DOW Loop in an RPG Loan-Payment Procedure
P CalcPayments B D CalcPayments PI 5U 0 D* Parameters D loan 5P 0 VALUE D payment 5P 0 VALUE D* Local fields D payments S 5U 0 INZ(0) D remaining S 5P 0 INZ(0) D paid S 5P 0 INZ(0) D output S 32A C* Calculate and display payments C EVAL remaining = loan C DOW remaining > 0 C IF ((paid + payment) > loan) C EVAL payment = loan - paid C ENDIF C EVAL paid = paid + payment C EVAL remaining = loan - paid C EVAL payments = payments + 1 C EVAL output = %CHAR(paid) + ', ' + C %CHAR(remaining) C output DSPLY C ENDDO C RETURN payments P CalcPayments E
This loops while there is a remainder left to pay. Each iteration of the DOW loop first checks if the new payment will reduce the debt to or below zero. If so, it changes this month's payment to just what's left. The remaining logic evaluates the total paid so far by adding to it this month's payment, and evaluates the new amount remaining by deducting this month's payment. It also bumps up the total payments counter, and finally prints the amount paid and amount remaining. We don't show the prototype of this procedure for simplicity, but here is the mainline code to call it:
C EVAL payments = CalcPayments(335:100) C payments DSPLY C EVAL payments = CalcPayments(200:100) C payments DSPLY
The output of this is as you would expect:
100, 235 200, 135 300, 35 335, 0 4 100, 100 200, 0 2
The equivalent in Java is the while statement, which has the following syntax:
while (expression) { // statement(s) }
Like the if and for statements, the braces around the body are required if the body is more than one statement, and optional otherwise. The expression is any boolean expression evaluating to true or false. The loop iterates as long as the expression is true. Listing 4.11 is the same code as Listing 4.10, but in OO style it is a class that uses instance variables, versus passing parameters.
Listing 4.11: A while Loop in a Java Loan-Payment Class
public class Loan { private int loan; // amount of loan private int payment; // amount per payment public Loan(int loan, int payment) // constructor { this.loan = loan; this.payment = payment; } public int calcPayments() { int payments = 0; int remaining = loan; int paid = 0; while (remaining > 0) { if ((paid + payment) > loan) payment = loan - paid; paid += payment; remaining = loan - paid; ++payments; System.out.println(paid + ", " + remaining); } return payments; } public static void main(String args[]) { Loan loanTest1 = new Loan(335,100); int payments = loanTest1.calcPayments(); System.out.println("============"); System.out.println("payments: " + payments); System.out.println(); Loan loanTest2 = new Loan(200,100); payments = loanTest2.calcPayments(); System.out.println("============"); System.out.println("payments: " + payments); } // end main } // end of class Loan
The output of this is exactly the same as the output of the RPG version. (Note that the contracted += operator from the previous chapter is used to add payment to paid.)
In Java, the expression is very often a variable of type boolean (described in Chapter 5), which itself evaluates to true or false. This makes it a perfect match for the condition expression. The variable is usually declared and initialized to true first, then set to false inside the loop when some ending condition is met, such as reading the end of the file:
boolean notDone = true; while (notDone) { // read input if (input.eof()) // end of file for input? notDone = false; }
In fact, you will usually see the boolean variable initialized to false and its negation checked in the condition expression, which accomplishes the same thing:
boolean done = false; while (!done) { // read input if (input.eof()) // end of file for input? done = true; }
The while loop is actually equivalent to the for loop; each can be completely mapped to the other. All you do is move the initialization part of the for loop outside, preserve the condition expression, and move the incrementing part to the bottom of the while loop body. Here is a typical for loop:
for (int x = 0; x < myArray.length; x++) myArray[x] = 1;
Here is the exact equivalent, but as a while loop:
int x = 0; while (x < myArray.length) myArray[x++] = 1;
So, when do you use the for loop and when do you use the while loop? It is your decision, really, because they are functionally equivalent. In general, though, the for loop is used when you can predict the number of iterations of the loop, and while is used when you cannot.
The last loop structure covered here is the do until loop. This is the DOU op-code in RPG, and the do while statement in Java. Here is RPG's DOU syntax:
C* FACTOR 1 OPCODE EXTENDED-FACTOR 2 C* ———————— —————— ———————————————— C DOU Expression C**** loop body C ENDDO
Except for its name, the DOU op-code is exactly the same as DOW. In the DOU operation, just like DOW, you specify an expression in the free-form factor two. If DOW and DOU are so similar, why have two of them? The DOU operation expression is evaluated after the body of the structure has been executed, in contrast to before for DOW.
Here is Java's equivalent, the do while statement (note the ending semicolon):
do { // statement(s) } while (expression);
As with RPG's DOW versus DOU, Java's do while statement is functionally identical to the while statement. The difference is that the condition expression is not evaluated until the end of the loop, which guarantees at least one iteration of the loop. Our experience is that programmers tend to use one or the other of these exclusively. That underlines their equivalent functionality.
Consider the job of reading input from a file. (Hey, you've done that, right?) You have to read until the end of the file. Which loop structure is best? It's up to you. Both have pros and cons. If you use while, you must do the initial read outside the loop to initialize the end-of-file flag. Then, you must do the subsequent reads inside the loop, as follows:
boolean eof; // a variable that will be true or false eof = myFile.getRecord(); // get the first record from the file while (!eof) { // ... process record eof = myFile.getRecord(); }
The disadvantage here is clear. You have to duplicate the read statement. That can create bugs later on if that statement is changed in one place but not the other. Alternatively, you could code this using do while (or DOU in RPG), and avoid the redundancy, as follows:
boolean eof; // a variable that will be true or false do { eof = myFile.getRecord(); if (!eof) { // ... process record } } while (!eof);
The disadvantage of this approach is that the if statement adds complexity to ensure that the last read does not result in the end of the file. Again, the choice is completely a personal decision. (Our preference is while versus do while, but what do we know!)
Let's now turn our attention to those statements that directly alter flow of control by explicitly transferring control to another statement.
You have seen the looping constructs in RPG and Java, and how their termination is determined by a conditional expression at the top or bottom of the loop. The language runtime evaluates the expression at each iteration of the loop, and continues iterating as long as the expression does not evaluate to false.
Controlling when the loop ends is usually a simple matter of setting the appropriate variables to force the expression evaluation to false. This is the preferred and structured approach. However, for reasons of completeness, both languages offer alternative shortcuts to this. They take the form of the LEAVE op-code in RPG and the break statement in Java. Both of these, when used in the body of a switch, while, do, or for structure, force the immediate end of the loop. Control is then passed by default to the statement after the loop structure. The Java break statement also allows an optional tag parameter to force the exit of a loop labeled with that tag. This is for nested loops.
In addition, both languages offer a shortcut to force iteration of the loop from within the body, thus skipping all subsequent code in the body. This is done using the ITER op-code in RPG and the continue statement in Java. The continue statement, like the break statement, allows an optional tag value to explicitly identify the outer loop to be iterated. The continue statement is valid inside while, do, and for structures in Java. Both break and continue apply to the innermost loop structure by default. That is, they exit or iterate the innermost loop. In a nested loop, to exit or iterate a more outer loop, you use the optional tag value to skip to a labeled outer loop.
We caution you that both of these constructs for forcing termination and iteration offer nothing new in function over a little bit of proper if-else coding, and can be avoided. They make for some nasty maintenance problems, and are really nothing but special-case goto statements. Some people avoid them altogether. Other people insist that they can offer a more readable and elegant-looking solution than the nasty use of conditioning. Our preference is to not use them by default. In our years of experience with C and C++ programming, where they also exist, we have found them to be very rarely worth using. But then again, they make up another tool to put in your programming toolbox, so Listing 4.12 shows an RPG IV example using ITER and LEAVE.
Listing 4.12: RPG's ITER and LEAVE Op-codes in the FindLastChar Procedure
*890123456789012345678901234567890123456789012345678901234567890 D* Local fields D pos S 5U 0 D* Prototype for procedure: FindLastChar D FindLastChar PR 5U 0 D inString 32767A VARYING CONST C* Mainline code C EVAL pos = FindLastChar(' a test ') C pos DSPLY C EVAL pos = FindLastChar(' ') C pos DSPLY C* End the program C EVAL *INLR = *ON P*————————————————————————————— P* Procedure name: FindLastChar P* Purpose: Find position of last non-blank character P*————————————————————————————— P FindLastChar B D FindLastChar PI 5U 0 D inString 32767A VARYING CONST D* Local fields D index S 5U 0 D lastNonBlank S 5U 0 INZ(0) C* Code C FOR index = %LEN(inString) C BY 1 DOWNTO 1 C IF %SUBST(inString:index:1) = ' ' C ITER C ELSE C EVAL lastNonBlank = index C LEAVE C ENDIF C ENDFOR C RETURN lastNonBlank P FindLastChar E
In this example, a procedure named FindLastChar takes any character field or literal as input, and returns to the caller the position of the last non-blank character, or zero if the input is all blanks. To find the last non-blank character, it uses the new FOR op-code to loop from the ending position of the string down to one. Inside the loop, it tests if the character at the current index position is blank or not, using the %SUBST (substring) built-in function. If it is, it uses ITER to keep looping. Otherwise, it has found what it wants, and so it records the index position in lastNonBlank and leaves the loop. Finally, it returns lastNonBlank to the caller. The mainline code tests the procedure by calling it with two literals, and displays the returned value. What you get when you run this is 7 and 0.
By the way, Listing 4.12 shows how to write procedures in RPG that will accept character fields of any length: by declaring the length as the maximum character field length (32767) and by coding the keywords VARYING and CONST on the parameter D-specs for both the prototype and actual interface.
Listing 4.13 is the same example written in Java, using its continue and break statements. Notice that to get the lengths of strings in Java, we use the Java-supplied method length, and to extract a character at a given zero-based position, we use charAt. Since zero is a valid character position in Java (the first), we have to return -1 for the special case of all-blank input. (Strings are covered in detail in Chapter 7.) The input lines are continue, which iterates the current for loop, and break, which exits the loop.
Listing 4.13: Java's break and continue Statements
public class TestBreak { public static void main(String args[]) { int pos; pos = findLastChar(" a test "); System.out.println(pos); pos = findLastChar(" "); System.out.println(pos); } /** Find last non-blank character position in a string */ public static int findLastChar(String inString) { int lastNonBlank = -1; for (int index = inString.length()-1; index >= 0; index—) { if (inString.charAt(index) == ' ') continue; else { lastNonBlank = index; break; } } return lastNonBlank; } }
The break and continue statements also allow an optional label to identify the loop to exit or iterate, as shown in Listing 4.14.
Listing 4.14: Labeled continue Statements in Java
outer: for (int classes = 0; classes < marksByClass.length; classes++) { for (int marks = 0; marks < marksByClass[0].length; marks++) { if (marksByClass[classes][marks] == -1) continue outer; else if (marksByClass[classes][marks] == -2) continue; totalPerClass[classes] += marksByClass[classes][marks]; countPerClass[classes] += 1; } // end inner for loop } // end outer for loop
This example is a snippet from the full sample in TestLabeledLoop.java on the CD-ROM included with this book. It computes the average marks for all classes, given a two-dimensional array of marks per student per class. If an array entry contains –1, it marks the end of the array for that class, and if it contains –2, it marks a student who has quit the class. Hence, while processing this two-dimensional array, we need to iterate the labeled outermost loop if we see –2, and iterate the innermost non-labeled loop if we see –1. This is done using continue with a tag (outer) that is used to label the outermost for loop. Note that loops are labeled the same as statements are labeled in CL programs. However, only loops can be labeled in Java, and only for the purpose of identifying them on continue or break.
This is typically used to force your way out of deeply nested loops, and is in contrast to the procedure of setting a variable and checking that variable in the remainder of the code in all structures.
The GOTO op-code or statement is an example of a transfer structure that does not follow structured programming design, and its use has fallen out of favor. In fact, Java designers decided to not even include it in the language, although the word is reserved. It is interesting that they took such a strong stand on GOTO while still advocating the use of break and continue, which are quite similar.
RPG IV, however, still has a GOTO op-code for historical reasons, and its syntax remains the same:
C 99 GOTO EXIT C EXIT TAG C EVAL *INLR = *ON
In this example, if indicator 99 is on, you want control to transfer to the EXIT statement, which is identified as a target by the TAG op-code. Notice that the IF operation is a cleaner and more structured replacement for the GOTO and TAG op-codes.
In RPG IV, the RETURN operation code does more than its predecessor in RPG III. The RETURN op-code is used to return a value to the caller from a procedure. Listing 4.15 shows an RPG procedure named Max that returns the larger of two integers, and its prototype. It also shows some mainline code to test it.
Listing 4.15: The RPG Max Procedure Showing the RETURN Statement
D* Global fields D result S 5I 0 D* Prototype for procedure: Max D Max PR 5I 0 D value1 5I 0 VALUE D value2 5I 0 VALUE * Code to call max C EVAL result = Max(100 : 200) C result DSPLY C EVAL result = Max(200 : 100) C result DSPLY C EVAL *INLR = *ON P*——————————————————————— P* Procedure name: Max P* Purpose: Return maximum of two integers P*——————————————————————— P Max B D Max PI 5I 0 D value1 5I 0 VALUE D value2 5I 0 VALUE C* Determine and return the maximum value C IF value1 > value2 C RETURN value1 C ELSE C RETURN value2 C ENDIF P Max E
The output of running this is 200, twice. The RETURN op-code in RPG uses the free- format factor-two version of the C-spec, and you can code any literal, field, or expression in factor two. The type of that literal, field, or expression must match the declared return type of the procedure, however, which is the length, type, and decimal positions specified on the PI (Procedure Interface) and PR (Procedure Prototype) D-specs for the procedure. If these values are blank, then the procedure does not return anything, and a RETURN statement with nothing in factor two is optional.
Listing 4.16 shows the same method, max, coded in Java and called from the main method with the same inputs.
Listing 4.16: The Java max Method Showing the return Statement
public class TestReturn { public static void main(String args[]) { int result; result = max(100, 200); System.out.println(result); result = max(200, 100); System.out.println(result); } public static int max(int value1, int value2) { return value1 > value2 ? value1 : value2; } }
This is a very compact version of max, using the conditional operator to decide which of the two parameters to return. It tests if value1 > value2, and if true, returns value1; otherwise, it returns value2. This highlights the fact that the value returned on Java's return statement can also be a literal, a variable, or indeed an expression. The type of literal, variable, or expression must match the return type declared on the signature line, which in this case is int. If void is coded there, you can optionally code a return statement with no value.
Like RPG, there can be more than one return statement in a method. However, in general, you should strive as much as possible to only have a single return statement except for very simple procedures or methods, such the RPG Max procedure. This is because you will often need to do some coding or cleanup before returning, and if you have multiple return statements, you have to duplicate that code, which leads to maintenance problems. More theoretically, the more exit points in a procedure or method, the less structured and predicable it is considered to be.
With the above discussion in mind, and the fact that procedures are so much better than subroutines, we point out that RPG has added a new op-code in V4R4: LEAVESR. This allows you to exit a subroutine prematurely at any point in it. This was added to the language simply because it was so heavily requested by programmers, but we still caution you to use it sparingly, and indeed to consider using procedures instead of subroutines.
Notice that, in the case of RPG, the RETURN operation behaves just like its counterpart Java statement only if it is used in a subprocedure. If, however, the RETURN op-code is used in the mainline code, its behavior is different than Java's. The following occurs if RETURN is used in the mainline C-specs in RPG IV:
Note |
This description is not a change from RPG III. Programmers used the RETURN operation code in the mainline to avoid total shutdown of the program and, thereby, increased their performance by avoiding the startup performance penalty. |
In Java, you can specify a return statement in your main method as well, which signifies the end of the program. It does not have the same implications, however. The program ends whether a return statement was entered in the main, or whether the end of code was reached in main. You will see in the discussion of threads in Chapter 11, however, that there is a better way to exit your programs than to use return. We recommend using System.exit(0);. This, much like setting on the LR indicator in RPG, causes total and complete termination of the program.
This chapter introduced you to the control flow statements in Java and compared them with RPG's. It covered the following points:
Foreword