FOR
{ [INDEX] [Numeric-variable = Start [TO
Limit] [STEP Increment]] |
[TABLE] Table-variable (Index name [,
Index name]…) |
[EACH] Coded-variable} TESTNEW}
[WHERE condition] [WHILE condition] [UNTIL
condition];
Statements
END
FOR;
The FOR statement is used in situations such as processing every element of a table.
Thus
DEFINE Area DATA(
MonthlySales(12) MONEY(5,2));
FOR Work.Mth = 1 TO
12;
PRINT (Area.MonthlySales(Work.Mth));
END FOR;
prints each value of Monthly sales. Mth will have been defined somewhere, preferably as a SMALLINT field: if Jazz can’t find a suitable definition it will create one. A number of pre-defined fields like IX1 are defined for you in JZ2, allowing you to simply refer to them.
It is helpful to think of there being several formats for a FOR statement: FOR INDEX, FOR TABLE, FOR EACH, FOR WHILE, and FOR UNTIL. The keywords INDEX, TABLE, and EACH are optional **, with Jazz working out which kind of statement based on the type of the initial variable: -
a. If the variable has a CODES option, then this is a FOR EACH statement
b. If the variable has one or more dimensions, then this is a FOR TABLE statement
c. If the variable is a scalar number (i.e., has no unspecified dimensions), then this a FOR INDEX statement.
** INDEX, TABLE, and EACH will be interpreted as field names if possible. For example
DEFINE
AMax PARAMETERS ANY DATA(
Table(100) DECIMAL(7,2) INPUT,
MaxValue
DECIMAL(7,2) OUTPUT);
FOR Table(IX1);
becomes
FOR
AMax.Table(JZ2.IX1);
WHILE and UNTIL options can be used with any of these three forms, and like EACH, TABLE, or INDEX will terminate the loop. Thus
FOR Work.Mth = 1 TO
12 UNTIL SALES(Work.Mth) > 1000;
You can also use WHILE and UNTIL on their own, for example
FOR WHILE EZT-OU278A00-Data.WFIM <>
'*';
This is a
loop that will be executed repeatedly, until an iteration finds the condition
to be false. UNTIL is exactly
the same except that the loop will be terminated when the condition is true.
WHERE is neither: the loop will be controlled by it’s other conditions, but on every iteration the WHERE condition is tested, and if it is false that iteration will be
skipped. Thus
with the FOR above,
FOR Work.Mth = 1 TO
12 UNTIL SALES(Work.Mth) > 1000;
If SALES(Work.Mth)
> 1000 is true when Work.Mth = 5 then the loop will only
process the first 4 values. However with
FOR Work.Mth = 1 TO
12 WHERE SALES(Work.Mth) > 1000;
all 10 values will be examined, but only those for which the condition is true are processed by the statements between FOR and END FOR;
The various forms of FOR statement are used like this: -
FOR [INDEX] Numeric-variable = Start [TO
Limit] [STEP Increment]
This is the common form where you specify the starting and ending value of the loop, e.g.
FOR IX1 = 1 TO 12;
The keyword INDEX is optional. It will usually be omitted as this form of FOR statement is then very similar to loop statements in other languages.
If the index variable does not exist, it will be created for you in the Jazz Sundry Data definition, JZ. Thus if you write
FOR IY = 1 TO 10;
END FOR;
This will
become
FOR JZ.IY = 1 TO 10;
END FOR;
Or you might use one of the predefined index variables named starting with IX, which become (e.g.) JZ2.IX1. See Predefined Fields below.
STEP defines the step increment: if omitted (as here), STEP(1) is assumed. It may be negative, in which case the loop “runs backwards”, e.g.
FOR JZ2.IX1 = 12 TO 1 STEP -1;
You can omit TO when you want the loop to be terminated by the UNTIL or WHILE condition, or by a statement within the loop. Since STEP will also default, to STEP 1,
FOR JZ2.IX1 = 1;
is actually a loop, not a single iteration of the following statements. There will be a message if there is no TO, UNTIL or WHILE condition.
Start, Limit, and Increment may all be numeric fields instead of numeric constants. For example
FOR JZ2.IX1 = RECORD.START TO RECORD.STOP STEP RECORD.STEP;
Jazz uses the values of these fields when the loop starts. Thus if the fields have these values on entry to the FOR: -
RECORD.START
= 5;
RECORD.STOP
= 10;
RECORD.STEP
= 2;
then this FOR loop is equivalent to
FOR JZ2.IX1 = 5 TO 10 STEP 2;
Any assignment to these fields has no effect on the loop once it has started. Thus even if the loop executes the IF statement setting RECORD.STOP to zero, the loop continues to execute until a value of JZ2.IX1 exceeds 10: -
FOR IX1 = RECORD.START TO RECORD.STOP STEP RECORD.STEP;
IF
RECORD.Value >
10 THEN
RECORD.STOP
= 0; [This DOES NOT stop
the loop
END
IF;
…
END FOR;
The numeric variable, IX1 in our example, must be a scalar SMALLINT field: there will be messages if it has a dimension, or another format.
You can refer to the numeric variable, but you should not change its value. Jazz uses hidden fields that you can’t reference to protect you from the errors that could result, but it can’t cover all error possibilities. Thus the following code can’t possibly be correct, but Jazz cannot detect the error: -
PROGRAM ScrewUp;
FOR IX1 = 1 TO 10;
PERFORM Routine1;
PRINT (TableValue(IX1));
END FOR;
ROUTINE Routine1;
FOR IX1 = 20 TO 25;
…
END FOR
On reading the first loop in ScrewUp, FOR IX1 = 1 TO 10;, one would naturally expect PRINT (TableValue(IX1)); to print the 1st, 2nd, 3rd, … 10th value of TableValue. However although the first loop will be executed 10 times, which is correct, it will attempt to print TableValue(26) every time, which is not.
FOR [TABLE] Table-Variable (Index name [, Index name]…)
Since FOR statements are often used to iterate through a table, Jazz provides a form especially for this. Write
FOR Table(IX1,
IX2)
to iterate through the 2-d table “Table” using indexes IX1 and IX2. Thus if you’ve defined Table as
DEFINE D DATA(
Table (5, 12) CHAR(10));
then this loop is equivalent to a pair of nested loops: -
FOR IX1
= 1 TO 5;
FOR IX2 = 1 TO 12;
…
END FOR;
END FOR;
For iterating through a table you should use this form of FOR rather than the first form. Its advantage is that the table bounds are automatically built into the loop, and that Jazz can check that you’ve given the correct number of indexes. Also, this form allows Jazz to generate efficient code, which it can’t always do with FOR INDEX (form 1), and an extra bonus: if you later change the dimensions of the table, the FOR loop will be automatically corrected when you next edit the program.
The index fields can be named anything you like, they do not have to be named IX1, IX2, etc. However fields with these names are always defined into definition JZ2 whether you use them for this purpose or not, and it is recommended that you follow a rule of naming indexes IX1 to IX7 wherever possible.
As with INDEX, the keyword TABLE is optional and may be omitted. It MUST be omitted you have a field named “table” anywhere in your program. For example if this is present: -
DEFINE
TablData PARAMETERS ANY DATA(
Table (100) DECIMAL(7,2) INPUT, …
and you want to use “FOR Table” to process your own table: -
DEFINE
MyData DATA(
MyTable (5,10,2)
SMALLINT, …
you can do this by writing
For
MyTable(IX1,
IX2, IX3);
which will become
FOR
MyData.MyTable(JZ2.IX1, JZ2.IX2, JZ2.IX3);
However if you write
For
Table MyTable(IX1, IX2, IX3);
“Table” will be interpreted as a reference to TablData.Table, and the FOR statement will be invalid.
To process a table “cross-section”, i.e. part of a table in which one or more subscripts are held constant, you can write a TABLE loop naming NULL as an index. For example, with IN1D2.Sales defined
Sales(3,7)
DECIMAL(7,2),
you can write
FOR IN1D2.Sales(NULL, JZ2.IX2);
to process the 7 members of a particular set of Sales. This FOR is now equivalent to the single FOR INDEX statement
FOR IX2
= 1 TO 7;
This is particularly useful when a table is defined within another table. For example: -
DEFINE W DATA(
Grp(3) GROUP,
Location CHAR(30),
NameInGrp(2) CHAR(37),
…
NameInGrp is actually a table with two dimensions, so you will get error messages unless references to it have both dimensions. Thus not only would PRINT(W.NameInGrp(JZ2.IX2)) cause an error message, so would FOR W.NameInGrp(JZ2.IX2);. However you can’t always put the references to W.NameInGrp within FOR W.NameInGrp(JZ2.IX2, JZ2.IX2). For example: -
FOR
W.Grp(JZ2.IX1);
PRINT(W.Location(JZ2.IX1));
FOR W.NameInGrp(NULL, JZ2.IX2);
PRINT(W.NameInGrp(JZ2.IX1,JZ2.IX2));
END FOR;
END FOR;
FOR [EACH] Coded-Variable
A loop may iterate through all the possible values of a coded variable. Thus with fields defined like this: -
DEFINE
Recrd DATA(
Sex CHAR(1) CODES(M:Male, F:Female),
Region
DECIMAL(1) CODES(1:'
you can write a loop like this to iterate through the values M:Male, and F:Female
FOR
EACH Recrd.sex;
PRINT
(Recrd.sex);
END
FOR;
Similarly
FOR
EACH Recrd.Region;
…
END
FOR;
would iterate through the values of Region.
The coded field must not have a dimension.
As before, the keyword EACH may be omitted.
This statement option is used when FOR is used with a PLINE statement.
FOR IN1D2.Sales(Null, JZ2.IX2) TESTNEW;
PLINE
(…,IN1D2.District
… ,IN1D2.Name … ,IN1D2.Sales(1,JZ2.IX2) …);
END FOR;
With IN1D2.Sales defined with dimension (3,7) the FOR loop prints 7 lines. Because of TESTNEW scalar (non-repeating) fields are not printed for dimensions 2 to 7 unless the line is on a new page.
District *----Name-----* *-Sales--*
Region 1
1 BLEE,ElliottLin 78.00
79.00
80.00
81.00
82.00
83.00
84.00
1 HANNAH,Honour 241.00
242.00
243.00
Without the TESTNEW option this report looks like this: -
Region 1 New Zealand
1 BLEE,ElliottLin 78.00
1 BLEE,ElliottLin 79.00
1 BLEE,ElliottLin 80.00
1 BLEE,ElliottLin 81.00
1 BLEE,ElliottLin 82.00
1 BLEE,ElliottLin
83.00
1 BLEE,ElliottLin 84.00
1 HANNAH,Honour 241.00
1 HANNAH,Honour 242.00
Adding a WHERE condition causes the loop to be skipped if the WHERE condition is true. Thus
FOR JZ2.IX = 1 TO 10 WHERE R.Table(JZ2.IX) <> 35;
PRINT …
END FOR;
loops varying JZ2.IX from 1 to 10, but skipping values of JZ2.IX for which R.Table(JZ2.IX) is 35. Thus if the values of R.Table are 1, 15, 30, 35, 48, 35, 70, 80, 35, 12 the PRINT will be executed 7 times, as it won’t be executed when JZ2.IX = 4, 6 or 9. The loop above is equivalent to
FOR JZ2.IX = 1 TO 10;
IF R.Table(JZ2.IX) <> 35;
PRINT …
END IF;
END FOR;
WHILE and UNTIL will
terminate the loop.
FOR JZ2.IX = 1 TO 10 UNTIL R.Table(JZ2.IX) = 35;
With the values of R.Table above this will execute the loop for JZ2.IX = 1 to 3, but on the fourth value the condition is true and the loop is terminated.
Whereas UNTIL terminates the loop when the condition is true, WHILE terminates it when the condition is false. With R.Table values as above,
FOR JZ2.IX = 1 TO 10 WHILE R.Table(JZ2.IX) = 35;
immediately terminates the FOR loop, so it is never executed. However if the values of R.Table had been 35, 35, 35, 35, 48, 35, 70, 80, 35, 12 then the loop would have been executed for JZ2.IX = 1 to 4, but be terminated for JZ2.IX = 5 when R.Table(JZ2.IX) = 48.
It may be valid to use WHILE, and UNTIL as the only FOR options. For example,
FOR WHILE EZT-TEST-Data.WS-IN-FINE-DEDUPLIC = 'N' & EZT-TEST-Data.WS-IN-ABBINATO-DEDUPLIC
= 'N' & DEDUPLIC.AN400-CD-NDG
<= CONVE.CONVE-CD-CONVENZIONATO;
IF DEDUPLIC.AN400-CD-NDG
= CONVE.CONVE-CD-CONVENZIONATO;
EZT-TEST-Data.WS-IN-ABBINATO-DEDUPLIC = 'S';
ELSE;
PERFORM LEGGI-DEDUPLIC;
END IF;
END FOR;
Note that
without an implied INDEX, TABLE, or EACH option
there is no implied loop increment or termination, so it is essential that the
loop body includes logic to terminate the loop when appropriate. Here PERFORM LEGGI-DEDUPLIC reads a record, and the logic of this routine
is
ROUTINE LEGGI-DEDUPLIC;
GET DEDUPLIC NEXT;
IF DEDUPLIC.$Endfile;
EZT-TEST-Data.WS-IN-FINE-DEDUPLIC = 'S';
DEDUPLIC.AN400-CD-NDG =
999999999999999999;
END IF;
END ROUTINE LEGGI-DEDUPLIC;
so that
when ENDFILE is reached (DEDUPLIC.$Endfile
is true) the WHILE condition will be false with the next
iteration of the FOR statement.
A FOR statement can use all of the options WHERE, WHILE, and UNTIL with the INDEX, TABLE, or EACH option. Conditions cannot use LIKE comparisons, although they can use the other “SQL form” conditions IN(value list) and BETWEEN.
Note that the order in which you write the statement options has no effect on execution: you get exactly the same results from
FOR JZ2.IX1
= 1 TO 20 UNTIL In1d.name = 'robert' WHERE IN1D.Sales(JZ2.IX1) > 1000;
as from
FOR JZ2.IX1 = 1 TO 20 WHERE IN1D.Sales(JZ2.IX1) > 1000 UNTIL In1d.name = 'robert' ;
and even
FOR JZ2.IX1 = 1 WHERE IN1D.Sales(JZ2.IX1) > 1000 UNTIL In1d.name = 'robert' TO 20;
The logic of a FOR statement with a positive STEP value and WHERE, UNTIL and WHILE options is as follows: -
Set Numeric-Variable to Initial Value
IF Numeric-Variable > Limit OR Until condition is true OR While condition is false THEN
GO TO ExitLoop
IF WHERE condition on FOR is false THEN
GO TO CycleLoop
Do statements from FOR to END FOR
CycleLoop:
Add Step Increment to Numeric Variable
GO TO
ExitLoop:
Continue with statements following END FOR
With a negative STEP value, the test is
IF Numeric-Variable < Limit
and Add Step Increment to Numeric Variable
=> Subtract Step Increment From Numeric Variable
Jazz has pre-defined a number of fields for you to use anywhere that you want an index (SMALLINT) or flag (BOOLEAN) field. For example, just write IX1, this will be qualified to JZ2.IX1. Although all these fields are known to Jazz, they will only be generated into the COBOL program if they are used.
JZ2 starts with 12 flags, named FLAG1 to FLAG10, plus FLAGNull and SEARCH-FOUND. For example
FLAG1
BOOLEAN VALUE false,
Like all BOOLEAN fields, they have initial value False. FLAGNull and SEARCH-FOUND are used by Jazz, you can refer to them but you shouldn’t set them.
Fields named IX, IX1 to IX10, and IXA to IXZ are defined like
IX
SMALLINT VALUE 0,
and are available for general use. Fields IXLS, IXL2, and IXNULL may be set by Jazz, like the special flags you can refer to them but you should not set them.