PERFORM VARYING and nested loops
PERFORM VARYING is COBOL's counting loop, the equivalent of for in other
languages. It sets a counter, tests a condition, runs the body, and steps the
counter:
IDENTIFICATION DIVISION.
PROGRAM-ID. VARYDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-I PIC 9(2).
PROCEDURE DIVISION.
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 3
DISPLAY "PASS " WS-I
END-PERFORM
DISPLAY "AFTER THE LOOP WS-I IS " WS-I
PERFORM VARYING WS-I FROM 10 BY -5 UNTIL WS-I < 1
DISPLAY "COUNTDOWN " WS-I
END-PERFORM
STOP RUN.
Output:
PASS 01
PASS 02
PASS 03
AFTER THE LOOP WS-I IS 04
COUNTDOWN 10
COUNTDOWN 05
The sequence is: set WS-I to the FROM value, test UNTIL, run the body,
add the BY value, test again. The test comes first (like TEST BEFORE), so
when the loop ends the counter has already gone one step past the last
value used. Here that is 4, not 3.
FROM and BY can be literals or data items, and BY can be negative.
The PIC 9 trap
The counter must be able to hold the value that ends the loop:
01 WS-I PIC 9.
* ...
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 9
WS-I can never be greater than 9. When it is 9 and the loop adds 1, the
result 10 is truncated to 0 and the loop starts again. It never ends.
GnuCOBOL warns you ('WS-I' may not be GREATER THAN 9), but many compilers
say nothing. Make counters one digit bigger than the limit.
Nested loops with AFTER
To walk every combination of two counters, add an AFTER clause. The AFTER
counter changes fastest, like the minutes hand of a clock:
IDENTIFICATION DIVISION.
PROGRAM-ID. GRID.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ROW PIC 9.
01 WS-COL PIC 9.
PROCEDURE DIVISION.
PERFORM 1000-SHOW-CELL
VARYING WS-ROW FROM 1 BY 1 UNTIL WS-ROW > 2
AFTER WS-COL FROM 1 BY 1 UNTIL WS-COL > 3
STOP RUN.
1000-SHOW-CELL.
DISPLAY "ROW " WS-ROW " COL " WS-COL.
Output:
ROW 1 COL 1
ROW 1 COL 2
ROW 1 COL 3
ROW 2 COL 1
ROW 2 COL 2
ROW 2 COL 3
For each value of WS-ROW, WS-COL restarts at 1 and runs to the end.
COBOL allows several AFTER clauses, which you will use for tables with
two or three dimensions in the next module.
AFTER is normally used with the out-of-line form, as above. For inline code,
nest two PERFORM VARYING ... END-PERFORM blocks instead:
PERFORM VARYING WS-ROW FROM 1 BY 1 UNTIL WS-ROW > 2
PERFORM VARYING WS-COL FROM 1 BY 1 UNTIL WS-COL > 3
DISPLAY "ROW " WS-ROW " COL " WS-COL
END-PERFORM
END-PERFORM
On the job
Loop counters are usually PIC S9(4) COMP (binary) rather than display
digits, because binary arithmetic is faster on the mainframe. You will
see names like WS-SUB, WS-IX or I in older code.
Your task
The loans department wants a quick interest lookup table. Read two lines:
- The largest loan amount, e.g.
3000 - The highest annual rate in whole percent, from 1 to 9, e.g.
2
For every loan amount from 1000 up to the largest amount in steps of 1000,
and for every rate from 1% up to the highest rate, display one line with
a year's simple interest (amount * rate / 100):
AMOUNT 1000 RATE 01% INTEREST 10.00
AMOUNT 1000 RATE 02% INTEREST 20.00
AMOUNT 2000 RATE 01% INTEREST 20.00
...
Amount is the outer loop and rate the inner loop. If the largest amount is under 1000, display nothing.
The starter has the paragraph that calculates and displays one line. Use a
single PERFORM ... VARYING ... AFTER to drive it.