Chapter 8: Iteration -- The PERFORM Statement in All Its Forms -- Key Takeaways
Chapter Summary
The PERFORM statement is the single most important control-flow mechanism in COBOL. It serves double duty as both the language's iteration construct and its primary mechanism for modular program organization. Unlike languages that separate looping (for, while) from subroutine calls, COBOL uses PERFORM for both purposes. This chapter examined every variant of PERFORM, from the simple one-shot PERFORM that transfers control to a paragraph, to the sophisticated PERFORM VARYING that implements counted loops with initialization, testing, and incrementing.
We began with the basic PERFORM, which executes a named paragraph or section once and then returns control to the statement following the PERFORM. This simple form is the backbone of structured COBOL programming, allowing the PROCEDURE DIVISION to be organized into small, focused paragraphs that are invoked as needed. PERFORM THRU extends this by executing a range of consecutive paragraphs, a technique widely used in legacy code but discouraged in modern practice due to its tight coupling to paragraph ordering.
The iterative forms of PERFORM were the core focus of this chapter. PERFORM TIMES executes a block a fixed number of times. PERFORM UNTIL repeats execution as long as a condition remains false (or true, when WITH TEST AFTER is specified). PERFORM VARYING adds automatic initialization and incrementing of a loop-control variable, closely resembling the for loop of other languages. We also covered inline PERFORM, which places the loop body directly between PERFORM and END-PERFORM rather than referencing a separate paragraph, and nested PERFORM VARYING with the AFTER phrase for iterating through multi-dimensional structures.
Key Concepts
- A basic PERFORM executes a named paragraph once and returns control to the calling point, functioning as COBOL's equivalent of a subroutine call.
- PERFORM THRU executes all paragraphs from a starting paragraph through an ending paragraph in sequence; it relies on physical paragraph ordering in the source code.
- PERFORM TIMES executes a paragraph or inline block a fixed number of times, specified by an integer literal or a numeric data item.
- PERFORM UNTIL repeats execution until a specified condition becomes true; the condition is tested BEFORE each iteration by default.
- WITH TEST BEFORE (the default) tests the condition before executing the loop body, meaning the body may never execute if the condition is initially true.
- WITH TEST AFTER tests the condition after executing the loop body, guaranteeing the body executes at least once regardless of the initial condition.
- PERFORM VARYING initializes a loop-control identifier, tests a condition, executes the loop body, and increments the identifier, all in a single statement.
- The AFTER phrase in PERFORM VARYING adds inner loop variables, creating nested loop behavior within a single PERFORM statement.
- Inline PERFORM places the loop body directly between the PERFORM and END-PERFORM keywords, eliminating the need for a separate paragraph when the logic is short.
- END-PERFORM is the explicit scope terminator for inline PERFORM blocks and must always be coded when using inline PERFORM.
- PERFORM can invoke paragraphs that themselves contain PERFORM statements, enabling modular program decomposition through nested calls.
- The EXIT PERFORM statement terminates an inline PERFORM loop prematurely, similar to the break statement in other languages.
- EXIT PERFORM CYCLE skips the remainder of the current iteration and proceeds to the next iteration, similar to the continue statement in other languages.
- The loop-control variable in PERFORM VARYING retains its final value after the loop terminates, which is useful for detecting whether a search completed successfully.
- Recursive PERFORM (a paragraph PERFORMing itself) is not supported in standard COBOL and leads to undefined behavior.
Common Pitfalls
- Infinite loops from incorrect UNTIL conditions: Writing
PERFORM UNTIL WS-EOFwithout ever setting WS-EOF to TRUE inside the loop creates an infinite loop. Always ensure the loop body modifies the condition variable. - Off-by-one errors with PERFORM VARYING: Forgetting that the loop-control variable is incremented before the condition is re-tested can lead to processing one too many or one too few iterations. Trace through the logic carefully.
- Using PERFORM THRU in new code: PERFORM THRU creates fragile dependencies on paragraph ordering. Inserting a new paragraph between the start and end paragraphs inadvertently includes it in the range. Prefer PERFORMing individual paragraphs.
- Mixing inline and out-of-line PERFORM styles inconsistently: Using inline PERFORM for lengthy logic and out-of-line PERFORM for trivial logic makes programs harder to read. Establish a consistent convention, such as using inline PERFORM only for loops shorter than ten statements.
- Modifying the VARYING identifier inside the loop body: Changing the loop-control variable within the loop body interferes with the automatic incrementing behavior and produces unpredictable iteration counts.
- Forgetting WITH TEST AFTER for read loops: The standard sequential file read loop requires reading a record before testing for end-of-file. Using WITH TEST BEFORE requires a priming read before the loop. Using WITH TEST AFTER allows the read and test to be combined naturally.
- Omitting END-PERFORM: Forgetting the END-PERFORM terminator for an inline PERFORM causes the compiler to interpret subsequent statements as part of the loop body, leading to confusing compilation errors or incorrect runtime behavior.
Quick Reference
* Basic PERFORM (execute paragraph once)
PERFORM 1000-INITIALIZE
* PERFORM THRU (legacy style)
PERFORM 2000-PROCESS THRU 2000-PROCESS-EXIT
* PERFORM TIMES
PERFORM 3000-PRINT-HEADER 3 TIMES
* PERFORM UNTIL (with priming read)
READ INPUT-FILE INTO WS-RECORD
AT END SET WS-EOF TO TRUE
END-READ
PERFORM UNTIL WS-EOF
PERFORM 4000-PROCESS-RECORD
READ INPUT-FILE INTO WS-RECORD
AT END SET WS-EOF TO TRUE
END-READ
END-PERFORM
* PERFORM WITH TEST AFTER
PERFORM WITH TEST AFTER
UNTIL WS-RESPONSE = "Q"
DISPLAY "Enter choice (Q to quit): "
ACCEPT WS-RESPONSE
PERFORM 5000-HANDLE-CHOICE
END-PERFORM
* PERFORM VARYING (counted loop)
PERFORM VARYING WS-INDEX
FROM 1 BY 1
UNTIL WS-INDEX > WS-TABLE-SIZE
DISPLAY WS-TABLE-ENTRY(WS-INDEX)
END-PERFORM
* PERFORM VARYING with AFTER (nested loops)
PERFORM VARYING WS-ROW FROM 1 BY 1
UNTIL WS-ROW > WS-MAX-ROWS
AFTER WS-COL FROM 1 BY 1
UNTIL WS-COL > WS-MAX-COLS
MOVE ZERO TO WS-MATRIX(WS-ROW, WS-COL)
END-PERFORM
* EXIT PERFORM and EXIT PERFORM CYCLE
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > 100
IF WS-ITEM(WS-I) = WS-SEARCH-KEY
MOVE WS-I TO WS-FOUND-INDEX
EXIT PERFORM
END-IF
IF WS-ITEM(WS-I) = SPACES
EXIT PERFORM CYCLE
END-IF
END-PERFORM
What's Next
Chapter 9 tackles string handling and character manipulation in COBOL. You will learn to concatenate strings with STRING, parse delimited data with UNSTRING, inspect and transform characters with INSPECT, and access substrings through reference modification. These string operations frequently appear inside PERFORM loops, so the iteration techniques from this chapter will be applied immediately as you learn to process character data field by field and record by record.