2000-PROCESS-RECORD.
*
PERFORM 2900-READ-NEXT-RECORD.
*
IF EOF
GO TO 2000-EXIT
ELSE IF JK-ORD-REC
PERFORM 2700-PROCESS-ORD-REC
GO TO 2000-PROCESS-RECORD.
*
IF NOT ORD-POSTED
GO TO 2000-PROCESS-RECORD.
*
IF JK-AFTER-ZIP IS NUMERIC
AND JK-AFTER-ZIP NOT = ALL ZEROS
GO TO 2000-PROCESS-RECORD.
*
PERFORM 2100-REPORT-BAD-ZIP.
GO TO 2000-PROCESS-RECORD.
*
2000-EXIT.
EXIT.
One way to untangle this loop is to rewrite it as one or more loops
nested inside another.
A purely mechanical transformation of the code may not be very satisfying if it does not reflect a sound instinct for what the program is trying to do. Let us therefore consider this example in more detail.
Each order record carries a flag indicating whether the order is posted, cancelled, or in error status. It also carries various other scraps of information about the type of customer and the kind of service provided.
Each WTN record contains a ZIP code field reflecting the geographical location of the telephone. Usually all the line numbers for a given account are at the same location, but not always. Specialized vendor software assigns ZIP codes on the basis of the customer's address. Sometimes, however, this software can't assign a ZIP code, for any of various reasons. These cases require manual intervention.
The example program is supposed to report all the line numbers to which we have been unable to assign a ZIP code. In these cases the ZIP code field is filled either with zeros or blanks.
However, we only care about posted orders. Cancelled orders obviously don't matter, and the orders in error status, by definition, are invalid anyway. Moreover, we don't care about mobile telephones, since a ZIP code is meaningless for them. We can screen out all of these irrelevancies on the basis of information in the order record.
2000-PROCESS-ORDER.
*
* find the next order record, if we don't already have one
*
PERFORM 2900-READ-NEXT-RECORD
UNTIL EOF OR JK-ORD-REC.
*
* process it; then read past it
*
IF NOT EOF
PERFORM 2700-PROCESS-ORD-REC
PERFORM 2900-READ-NEXT-RECORD
*
* process each WTN record in an eligible order
*
IF (ORD-POSTED AND NOT ORD-MOBILE-SERVICE)
PERFORM UNTIL EOF OR JK-ORD-REC
*
IF JK-AFTER-ZIP NOT NUMERIC
OR JK-AFTER-ZIP = ALL ZEROS
PERFORM 2100-REPORT-BAD-ZIP
END-IF
PERFORM 2900-READ-NEXT-RECORD
*
END-PERFORM.
*
* either exit the loop or continue
*
IF EOF
GO TO 2000-EXIT
ELSE
GO TO 2000-PROCESS-RECORD.
*
2000-EXIT.
EXIT.
The revised version has two inner loops, coded in-line in order to
make the nesting explicit. In a real program I would probably pull
the second loop into a separate paragraph.
In any case, only one recurrent GO TO remains.
This very tedium illustrates my main point.
When you restructure a loop into nested loops, you have to pay close attention to the dirty details of the application. You can't just rearrange the code mechanically. That's why I had to explain so many boring details.
Because this process is not mechanical, it is error-prone. I designed this example to be simple, but it still took me a while to get it right. At least I think I got it right.