Counter-Controlled Loops with PERFORM VARYING
PERFORM ... VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > WS-LIMIT is COBOL's standard counting loop, initializing the index once, testing the UNTIL condition before each pass by default, and incrementing the index by the BY value after every iteration, which makes it functionally equivalent to a for-loop in languages like C or Java. The index field, often used to subscript a table with WS-ARRAY(WS-INDEX), can be any numeric working-storage field or, in table-handling contexts, an actual INDEXED BY index-name, which the compiler can sometimes optimize more efficiently than an ordinary data item. Because the UNTIL condition is checked before the loop body by default, a loop bound like FROM 1 BY 1 UNTIL WS-INDEX > 10 runs the body for index values 1 through 10 inclusive, a common off-by-one trap for programmers assuming UNTIL means the same as a less-than-or-equal-to condition.
Cricket analogy: PERFORM VARYING WS-OVER FROM 1 BY 1 UNTIL WS-OVER > 20 mirrors bowling through a full T20 innings over by over, incrementing the over count after each one and stopping once over 20 completes.
01 WS-SALES-TABLE.
05 WS-MONTH-SALES OCCURS 12 TIMES PIC 9(7)V99.
01 WS-INDEX PIC 9(2).
01 WS-ANNUAL-TOTAL PIC 9(9)V99 VALUE ZERO.
PROCEDURE DIVISION.
PERFORM VARYING WS-INDEX FROM 1 BY 1
UNTIL WS-INDEX > 12
ADD WS-MONTH-SALES(WS-INDEX) TO WS-ANNUAL-TOTAL
END-PERFORM.
DISPLAY "ANNUAL TOTAL: " WS-ANNUAL-TOTAL.Nested Loops with the AFTER Phrase
PERFORM VARYING supports an AFTER phrase that lets a single statement drive multiple nested loops without writing them as physically nested PERFORM blocks, for example PERFORM VARYING WS-ROW FROM 1 BY 1 UNTIL WS-ROW > 10 AFTER WS-COL FROM 1 BY 1 UNTIL WS-COL > 10 varies WS-COL completely through its range for every single value of WS-ROW, exactly like a nested for-loop where WS-COL is the inner loop. Multiple AFTER phrases can be chained to control three or more levels of nesting in one statement, which is common when processing multi-dimensional tables such as a matrix or a table of tables. This single-statement form is functionally identical to writing genuinely nested PERFORM ... VARYING blocks, but it keeps the loop control compact and makes the nesting relationship explicit in one place rather than spread across multiple END-PERFORM terminators.
Cricket analogy: PERFORM VARYING WS-MATCH FROM 1 BY 1 UNTIL WS-MATCH > 10 AFTER WS-OVER FROM 1 BY 1 UNTIL WS-OVER > 20 mirrors iterating through every over of every match in a 10-match IPL group stage, cycling all 20 overs before moving to the next match.
01 WS-MATRIX.
05 WS-ROW OCCURS 3 TIMES.
10 WS-CELL OCCURS 3 TIMES PIC 9(3).
01 WS-R PIC 9 VALUE 1.
01 WS-C PIC 9 VALUE 1.
PROCEDURE DIVISION.
PERFORM VARYING WS-R FROM 1 BY 1 UNTIL WS-R > 3
AFTER WS-C FROM 1 BY 1 UNTIL WS-C > 3
DISPLAY "CELL(" WS-R "," WS-C ") = " WS-CELL(WS-R, WS-C)
END-PERFORM.Sentinel-Controlled Read Loops
The most common loop pattern in production COBOL is the read-until-end-of-file loop, which reads one record, tests an end-of-file flag typically set by an AT END clause, and repeats until that flag indicates no more data. Because the READ statement must execute before the AT END condition can be known, this pattern is a textbook use case for PERFORM ... WITH TEST AFTER, or equivalently a priming read before a WITH TEST BEFORE loop, both of which guarantee the READ happens before the flag is ever checked, avoiding the classic bug where a WITH TEST BEFORE loop checks an uninitialized end-of-file flag before any record has actually been read. Sequential file processing programs, sorts, and report generators in COBOL almost universally follow this same sentinel-loop shape, making it one of the most important loop patterns to recognize when reading or writing production code.
Cricket analogy: A sentinel read loop mirrors an umpire checking after every ball whether all ten wickets have fallen, continuing to signal for the next delivery until that end-of-innings condition becomes true.
A WITH TEST BEFORE loop that checks an end-of-file flag before any READ has ever executed will read that flag in its initial VALUE state, typically "N" or spaces, and enter the loop body correctly on the first pass — but only if the flag was properly initialized. If the flag is left uninitialized, its starting value is unpredictable, and the loop may either skip entirely or process garbage data. Always initialize end-of-file flags explicitly, and prefer WITH TEST AFTER or an explicit priming READ before the loop to make the control flow unambiguous.
EXIT PERFORM and Avoiding Infinite Loops
EXIT PERFORM immediately terminates the innermost enclosing in-line PERFORM loop, transferring control to the statement after END-PERFORM, which is useful for breaking out of a loop early when a condition is discovered partway through the body that the UNTIL clause alone can't express cleanly; EXIT PERFORM CYCLE, by contrast, skips the remainder of the current iteration and jumps straight to the loop's condition test, acting like a continue statement rather than a break. Every conditional loop carries the risk of never terminating if its UNTIL condition depends on a field that the loop body never actually updates, which is a common bug when a programmer forgets to increment an index or set an end-of-file flag inside the loop; because COBOL has no automatic loop-bound safety net, an infinite PERFORM UNTIL will run until the job is manually cancelled or the system resource limits are hit. Defensive COBOL style often adds a secondary safety counter alongside the primary business condition, such as UNTIL WS-EOF = "Y" OR WS-SAFETY-COUNT > 999999, specifically to guarantee termination even if the primary condition logic has a bug.
Cricket analogy: EXIT PERFORM is like a captain calling off a rain-affected match early once conditions make further play pointless, breaking out of the scheduled overs loop before the natural end condition is met.
PROCEDURE DIVISION.
PERFORM VARYING WS-INDEX FROM 1 BY 1
UNTIL WS-INDEX > WS-TABLE-SIZE
IF WS-CUSTOMER-ID(WS-INDEX) = WS-SEARCH-ID
MOVE WS-INDEX TO WS-FOUND-INDEX
EXIT PERFORM
END-IF
ADD 1 TO WS-INDEX
END-PERFORM.- PERFORM VARYING FROM/BY/UNTIL is COBOL's counting loop and by default tests the condition before each iteration.
- The AFTER phrase drives nested loops from a single PERFORM statement, with multiple AFTER phrases supporting deeper nesting.
- Sentinel read loops (read, test AT END flag, repeat) are the most common pattern in sequential file processing.
- WITH TEST AFTER or a priming READ ensures the READ executes before the end-of-file flag is ever checked.
- EXIT PERFORM breaks out of the innermost in-line PERFORM loop immediately.
- EXIT PERFORM CYCLE skips to the next iteration's condition test, acting like a continue statement.
- A defensive safety counter alongside the primary UNTIL condition guards against infinite loops caused by logic bugs.
Practice what you learned
1. In PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 10, for which values of WS-INDEX does the loop body execute?
2. What does the AFTER phrase in PERFORM VARYING accomplish?
3. Why is a sentinel-controlled read loop typically written with WITH TEST AFTER or a priming READ?
4. What is the difference between EXIT PERFORM and EXIT PERFORM CYCLE?
Was this page helpful?
You May Also Like
PERFORM Statement Variations
Explore the many forms of COBOL's PERFORM statement, from simple out-of-line calls and PERFORM THRU to TIMES, UNTIL, and in-line PERFORM blocks.
Conditional Logic in COBOL
Master COBOL's IF/ELSE, nested conditions, the EVALUATE statement, and condition-names (88-levels) for expressing readable business rules.
MOVE and Arithmetic Statements
Learn how COBOL's MOVE statement reformats data between fields and how ADD, SUBTRACT, MULTIPLY, DIVIDE, and COMPUTE perform arithmetic with proper rounding and overflow handling.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics