Book Read Free

Michael Coughlan

Page 17

by Beginning COBOL for Programmers-Apress (2014) (pdf)


  Example 6-3. Model for a Gravity-Driven COBOL Program

  P1.

  statement

  statement

  statement

  P2.

  statement

  statement

  P3.

  statement

  IF cond GO TO P2

  statement

  statement

  IF cond GO TO P3

  P4.

  statement

  IF cond GO TO P2

  statement

  statement

  STOP RUN

  111

  Chapter 6 ■ Control StruCtureS: IteratIon

  Closed Subroutines

  A closed subroutine is a named block of code that can only be executed by invoking it by name. Control cannot

  “fall into” a closed subroutine. A closed subroutine can usually declare its own local data, and that data cannot be accessed outside the subroutine. Data in the main program can be passed to the subroutine by means of parameters specified when the subroutine is invoked. In C and Modula-2, procedures and functions implement closed subroutines.

  In Java, methods are used.

  COBOL Subroutines

  COBOL supports both open and closed subroutines. Open subroutines are implemented using the first format of the PERFORM verb. Closed subroutines are implemented using the CALL verb and contained or external subprograms.

  You learn about contained and external subprograms later in the book.

  ■ ISO 2002 ISo 2002 CoBol provides additional support for closed subroutines in the form of methods. Methods in CoBol bear a very strong syntactic resemblance to contained subprograms.

  Why Use Open Subroutines?

  The open subroutines represented by paragraphs (and sections) are used to make programs more readable and

  maintainable. Although PERFORMed paragraphs are not as robust as the user-defined procedures or functions found in other languages, they are still useful. They allow you to partition code into a hierarchy of named tasks and subtasks without the formality or overhead involved in coding a procedure or function. COBOL programmers who require the protection of that kind of formal partitioning can use contained or external subprograms.

  Partitioning a task into subtasks makes each subtask more manageable; and using meaningful names for the

  subtasks effectively allows you to document in code what the program is doing. For instance, a block of code that prints report headings can be removed to a paragraph called PrintReportHeadings. The details of how the task is being accomplished can be replaced with a name that indicates what is being done.

  Consider the partitioning and documentation benefits provided by the program skeleton in Example 6-4. The

  skeleton contains no real code (only PERFORMs and paragraph names), but the hierarchy of named tasks and subtasks allows you to understand that the program reads through a file containing sales records for various shops and for each shop prints a line on the report that summarizes the sales for that shop.

  Example 6-4. Program Skeleton

  PrintSummarySalesReport.

  PERFORM PrintReportHeadings

  PERFORM PrintSummaryBody UNTIL EndOfFile

  PERFORM PrintFinalTotals

  STOP RUN.

  PrintSummaryBody.

  PERFORM SummarizeShopSales

  UNTIL ShopId <> PreviousShopId

  OR EndOfFile

  PERFORM PrintShopSummary

  SummarizeShopSales.

  Statements

  112

  Chapter 6 ■ Control StruCtureS: IteratIon

  PrintReportHeadings.

  Statements

  PrintShopSummary.

  Statements

  PrintFinalTotals.

  Statements

  Obviously, it is possible to take partitioning to an extreme. You should try to achieve a balance between making the program too fragmented and too monolithic. As a rule of thumb, there should be a good reason for creating a paragraph that contains five statements or fewer.

  PERFORM NamedBlock

  This first format of the PERFORM (see Figure 6-1) is not an iteration construct. It simply instructs the computer to transfer control to an out-of-line block of code (that is, an open subroutine). The block of code may be a paragraph or a section. When the end of the block is reached, control reverts to the statement (not the sentence) immediately following the PERFORM.

  Figure 6-1. Metalanguage for PERFORM format 1

  In Figure 6-1, StartblockName and EndblockName are the names of paragraphs or sections. PERFORM..THRU

  instructs the computer to treat the paragraphs or sections from StartblockName TO EndblockName as a single block of code.

  PERFORM s can be nested. A PERFORM may execute a paragraph that contains another PERFORM, but neither direct nor indirect recursion is allowed. Unfortunately, this restriction is not enforced by the compiler, so a syntax error does not result; but your program will not work correctly if you use recursive PERFORMs.

  The order of execution of the paragraphs is independent of their physical placement. It does not matter where you put the paragraphs—the PERFORM will find and execute them.

  How PERFORM Works

  Listing 6-1 shows a short COBOL program that demonstrates how PERFORM works. The program executes as follows: 1. Control starts in paragraph LevelOne, and the message “Starting to run program” is

  displayed.

  2. When PERFORM LevelTwo is executed, control is passed to LevelTwo and the statements in

  that paragraph start to execute.

  3. When PERFORM LevelThree is executed, control passes to LevelThree. When PERFORM

  LevelFour is executed, the message “Now in LevelFour” is displayed.

  4. When the end of LevelFour is reached, control returns to the statement following the

  PERFORM that invoked it, and the message “Back in LevelThree” is displayed.

  113

  Chapter 6 ■ Control StruCtureS: IteratIon

  5. When LevelThree ends, control returns to the statement following the PERFORM, and the

  message “Back in LevelTwo” is displayed. Finally, when LevelTwo ends, control returns to

  paragraph LevelOne, and the “Back in LevelOne” message is displayed.

  6. When STOP RUN is reached, the program stops.

  Notice that the order of paragraph execution is independent of physical placement. For instance, although the paragraph LevelTwo comes after LevelThree and LevelFour in the program text, it is executed before them.

  As I mentioned earlier, although PERFORMs can be nested, neither direct nor indirect recursion is allowed. So it would not be valid for paragraph LevelThree to contain the statement PERFORM LevelThree . This would be direct recursion. Neither would it be valid for LevelTwo to contain the statement PERFORM LevelOne. This would be indirect recursion because LevelOne contains the instruction PERFORM LevelTwo.

  A frequent mistake made by beginning COBOL programmers is to forget to include STOP RUN at the end of

  the first paragraph. Example 6-5 shows the output that would be produced by Listing 6-1 if you forgot to include STOP RUN. From the output produced, try to follow the order of execution of the paragraphs.

  Example 6-5. Output when STOP RUN is missing

  > Starting to run program

  > > Now in LevelTwo

  > > > Now in LevelThree

  > > > > Now in LevelFour

  > > > Back in LevelThree

  > > Back in LevelTwo

  > Back in LevelOne

  > > > > Now in LevelFour

  > > > Now in LevelThree

  > > > > Now in LevelFour

  > > > Back in LevelThree

  > > Now in LevelTwo

  > > > Now in LevelThree

  > > > > Now in LevelFour

  > > > Back in LevelThree

  > > Back in LevelTwo

  Listing 6-1. Demonstrates How PERFORM Works

  IDENTIFICATION DIVISION.

  PROGRA
M-ID. Listing6-1.

  AUTHOR. Michael Coughlan.

  PROCEDURE DIVISION.

  LevelOne.

  DISPLAY "> Starting to run program"

  PERFORM LevelTwo

  DISPLAY "> Back in LevelOne"

  STOP RUN.

  LevelFour.

  DISPLAY "> > > > Now in LevelFour".

  LevelThree.

  114

  Chapter 6 ■ Control StruCtureS: IteratIon

  DISPLAY "> > > Now in LevelThree"

  PERFORM LevelFour

  DISPLAY "> > > Back in LevelThree".

  LevelTwo.

  DISPLAY "> > Now in LevelTwo"

  PERFORM LevelThree

  DISPLAY "> > Back in LevelTwo".

  PERFORM..THRU Dangers

  One variation that exists in all the PERFORM formats is PERFORM..THRU. When you use PERFORM..THRU, all the code from StartblockName to EndblockName is treated as a single block of code. Because PERFORM..THRU is generally regarded as a dangerous construct, it should only be used to PERFORM a paragraph and its immediately succeeding paragraph exit.

  The problem with using PERFORM..THRU to execute a number of paragraphs as one unit is that, in the

  maintenance phase of the program’s life, another programmer may need to create a new paragraph and may

  physically place it in the middle of the PERFORM..THRU block. Suddenly the program stops working correctly.

  Why? Because now PERFORM..THRU is executing an additional, unintentional, paragraph.

  Using PERFORM..THRU Correctly

  The warning against using PERFORM..THRU is not absolute, because when used correctly, PERFORM..THRU can be very useful.

  In COBOL there is no way to break out a paragraph that is the target of a PERFORM. All the statements have to be executed until the end of the paragraph is reached. But sometimes, such as when you encounter an error condition, you do not want to execute the remaining statements in the paragraph. This is a circumstance when PERFORM..THRU can be handy.

  Consider the program outline in Example 6-6. In this example, control will not return to Begin until SumEarnings has ended, but you do not want to execute the remaining statements if an error is detected. The solution adopted is to hide the remaining statements behind an IF NoErrorFound statement. This might be an adequate solution if there were only one type of error; but if there is more than one type, then nested IF statements must be used. This quickly becomes unsightly and cumbersome.

  Example 6-6. Using IFs to Skip Statements When an Error Is Detected

  PROCEDURE DIVISION.

  Begin.

  PERFORM SumEarnings

  STOP RUN.

  SumEarnings.

  Statements

  Statements

  IF NoErrorFound

  Statements

  Statements

  IF NoErrorFound

  Statements

  Statements

  Statements

  END-IF

  END-IF.

  115

  Chapter 6 ■ Control StruCtureS: IteratIon

  In Example 6-7, PERFORM..THRU is used to deal with the problem in a more elegant manner. The dangers of

  PERFORM..THRU are ameliorated by having only two paragraphs in the target block and by using a name for the second paragraph that clearly indicates that it is bound to the first.

  Example 6-7. Using PERFORM..THRU and GO TO to Skip Statements

  PROCEDURE DIVISION

  Begin.

  PERFORM SumEarnings THRU SumEarningsExit

  STOP RUN.

  SumEarnings.

  Statements

  Statements

  IF ErrorFound

  GO TO SumEarningsExit

  END-IF

  Statements

  Statements

  IF ErrorFound

  GO TO SumEarningsExit

  END-IF

  Statements

  Statements

  Statements

  SumEarningsExit.

  EXIT.

  When the statement PERFORM SumEarnings THRU SumEarningsExit is executed, both paragraphs are performed as if they are one paragraph. The GO TO jumps to the exit paragraph, which, because the paragraphs are treated as one, is the end of the block of code. This technique allows you to skip over the code that should not be executed when an error is detected.

  The EXIT statement in SumEarningsExit is a dummy statement. It has absolutely no effect on the flow of control. It is in the paragraph merely to conform to the rule that every paragraph must have one sentence. It has the status of a comment.

  The PERFORM..THRU and GO TO constructs used in this example are dangerous. GO TO in particular is responsible for the “spaghetti code” that plagues many COBOL legacy systems. For this reason, you should use PERFORM..THRU

  and GO TO only as demonstrated in Example 6-7.

  PERFORM..TIMES

  PERFORM..TIMES (see Figure 6-2) is the second format of the PERFORM verb.

  Figure 6-2. Metalanguage for PERFORM format 2

  116

  Chapter 6 ■ Control StruCtureS: IteratIon

  This format has no real equivalent in most programming languages, perhaps because of its limited usefulness. It simply allows a block of code to be executed RepeatCount#il times before returning control to the statement following PERFORM.

  Like the other formats of PERFORM, this format allows two types of execution:

  • Out-of-line execution of a block of code

  • Inline execution of a block of code

  Example 6-8 gives some example PERFORM..TIMES statements. These examples specify the RepeatCount using

  both literals and identifiers and show the inline and out-of-line variants of PERFORM.

  Example 6-8. Using PERFORM..TIMES

  PERFORM PrintBlankLine 10 Times

  MOVE 10 TO RepetitionCount

  PERFORM DisplayName RepetitionCount TIMES

  PERFORM 15 TIMES

  DISPLAY "Am I repeating myself?"

  END-PERFORM

  Inline Execution

  Inline execution will be familiar to programmers who have used the iteration constructs (while, do/repeat, for) of most other programming languages. An inline PERFORM iteratively executes a block of code contained within the same paragraph as the PERFORM. That is, the loop body is inline with the rest of the paragraph code. The block of code to be executed starts at the keyword PERFORM and ends at the keyword END-PERFORM (see Listing 6-2).

  Listing 6-2. Demonstrates PERFORM..TIMES and Inline vs. Out-of-Line Execution

  IDENTIFICATION DIVISION.

  PROGRAM-ID. Listing6-2.

  AUTHOR. Michael Coughlan.

  *> in-line and out-of-line PERFORM..TIMES

  DATA DIVISION.

  WORKING-STORAGE SECTION.

  01 NumOfTimes PIC 9 VALUE 5.

  PROCEDURE DIVISION.

  Begin.

  DISPLAY "About to start in-line Perform"

  PERFORM 4 TIMES

  DISPLAY "> > > > In-line Perform"

  END-PERFORM

  DISPLAY "End of in-line Perform"

  DISPLAY "About to start out-of-line Perform"

  PERFORM OutOfLineCode NumOfTimes TIMES

  DISPLAY "End of out-of-line Perform"

  STOP RUN.

  OutOfLineCode.

  DISPLAY "> > > > > Out-of-line Perform".

  117

  Chapter 6 ■ Control StruCtureS: IteratIon

  ■ ANS 85 In-line PERFORMs were only introduced as part of the anS 85 CoBol specification. In older legacy systems, the loop body is always out of line.

  Out-of-Line Execution

  In an out-of-line PERFORM, the loop body is a separate paragraph or section. This is the equivalent, in other languages, of having a procedure, function, or method invocation inside the loop body of a while or for construct.

  When a loop is required, but only a few statements are involved, you should use an inline PERFORM. When

  a loop is required, and the loop body ex
ecutes some specific task or function, out-of-line code should be used.

  The paragraph name chosen for the out-of-line code should identify the task or function of the code.

  PERFORM..UNTIL

  PERFORM..UNTIL (see Figure 6-3) is the third format of the PERFORM verb. This format implements both pre-test and post-test iteration in COBOL. It is the equivalent of Java’s while and do..while or Pascal’s While and Repeat..Until looping constructs.

  Figure 6-3. Metalanguage for PERFORM format 3

  Pre-test and post-test iteration structures seem to be strangely implemented in many languages. Some languages confuse when the test is done with how the terminating condition is tested (Pascal’s While and Repeat structures, for example). In many languages, the test for how the loop terminates emphasizes what makes the loop keep going, rather than what makes it stop. Although this may make formal reasoning about the loop easier, it does not come across as an entirely natural way of framing the question. In your day-to-day life, you do not say, “Heat the water while the water is not boiled” or “Pour water into the cup while the cup is not full.”

  Pre-test and post-test looping constructs are one area where COBOL seems to have things right. Whether the loop is pre-test or post-test, it is separated from how the terminating condition is tested; and the test for termination emphasizes what makes the loop stop, rather than what makes it keep going. In COBOL you might write

  118

  Chapter 6 ■ Control StruCtureS: IteratIon

  PERFORM ProcessSalesFile WITH TEST BEFORE

  UNTIL EndOfSalesFile

  or

  PERFORM GetNextCharacter WITH TEST AFTER

 

‹ Prev