MODULE 5 · LOOPS AND PROGRAM STRUCTURE · 3/7

PERFORM UNTIL, TEST BEFORE and TEST AFTER

14 min30 XPExercise

Most batch loops do not know in advance how many times they will run. They keep going until something happens: the input runs out, a balance hits a target, a user gets their PIN right.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. UNTILDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-BALANCE     PIC 9(5)V99 VALUE 100.00.
       01  WS-MONTHS      PIC 9(3) VALUE ZERO.
       PROCEDURE DIVISION.
           PERFORM UNTIL WS-BALANCE >= 150.00
               COMPUTE WS-BALANCE ROUNDED = WS-BALANCE * 1.10
               ADD 1 TO WS-MONTHS
           END-PERFORM
           DISPLAY "MONTHS: " WS-MONTHS " BALANCE: " WS-BALANCE
           STOP RUN.

Output: MONTHS: 005 BALANCE: 00161.05.

Read UNTIL carefully: the loop runs while the condition is false and stops as soon as it becomes true. If you come from another language, this is the opposite of while. Getting it backwards is a classic first-week bug.

The out-of-line form works the same way: PERFORM 2000-PROCESS UNTIL WS-EOF = "Y".

TEST BEFORE and TEST AFTER

By default COBOL checks the condition before each pass, so the body may run zero times. WITH TEST AFTER checks it at the end of each pass, so the body always runs at least once:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TESTDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-N           PIC 9(2) VALUE 5.
       PROCEDURE DIVISION.
           PERFORM WITH TEST BEFORE UNTIL WS-N > 3
               DISPLAY "BEFORE: " WS-N
           END-PERFORM
           PERFORM WITH TEST AFTER UNTIL WS-N > 3
               DISPLAY "AFTER: " WS-N
           END-PERFORM
           STOP RUN.

The condition is already true, so this prints only AFTER: 05. TEST BEFORE is the default and is almost never written out.

TEST AFTER suits "do it, then decide whether to go again", such as an ATM PIN prompt:

           PERFORM WITH TEST AFTER
                   UNTIL WS-PIN = "4321" OR WS-TRIES = 3
               ACCEPT WS-PIN
               ADD 1 TO WS-TRIES
           END-PERFORM

The priming read

The most important loop in batch COBOL reads input until a terminator. The standard shape is the priming read (or read-ahead): read the first record before the loop, and read the next one as the last step of the body.

           ACCEPT WS-AMOUNT
           PERFORM UNTIL WS-AMOUNT = ZERO
               ADD WS-AMOUNT TO WS-TOTAL
               ACCEPT WS-AMOUNT
           END-PERFORM

Why not read at the top of the body? Then the terminator itself (the 0) would be processed as if it were data before the UNTIL could stop the loop. With the priming read, the condition is always tested against a record that has not been processed yet. You will use this shape every time you read a file.

Infinite loops

If nothing inside the body can make the condition true, the loop never ends. On a mainframe the job runs until it hits its CPU time limit and is cancelled with an S322 abend, often after burning expensive CPU time. Always check that the body moves you towards the exit.

On the job

Loop conditions are usually written with an 88-level flag: PERFORM UNTIL END-OF-FILE. That reads like English and keeps the terminating value in one place.

Your task

A branch keys in the day's deposits, one amount per line, and ends the list with a line containing 0. Write a program that reads the amounts and displays how many deposits there were and their total:

DEPOSITS: 003
TOTAL:       1375.50

The terminating 0 is not a deposit and must not be counted. If the first line is 0, there were no deposits.

Use PERFORM UNTIL with a priming read: read the first amount before the loop, and read the next one at the end of the loop body.

fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.