7
\$\begingroup\$

I was long a professional in COBOL, but noone ever dared comment my style. As the FizzBuzz seems fashion here, I made one. Verbose, of course, we're speaking about COBOL. I know I could have made in 15 lines, but decided instead to provide the full structure for a production program. Result makes 87 lines.

I'll be happy to read your thoughts, comments, critics, or executions. Made on OpenCOBOLIDE.

  ******************************************************************
  * Author:Gazzz0X2Z                                              *
  * Date:2015, Dec the 12th                                      *
  * Purpose:FizzBuzz, what else?                                 *
  * Tectonics: cobc                                               *
  ******************************************************************
   IDENTIFICATION DIVISION.
  *-*-*-*-*-*-*-*-*-*-*-*-*-
   PROGRAM-ID. FIZZBUZZ.
  *-*-*-*-*-*-*-*-*-*-*--
   ENVIRONMENT DIVISION.
  *-*-*-*-*-*-*-*-*-*-*--
   CONFIGURATION SECTION.
  *----------------------
   INPUT-OUTPUT SECTION.
  *----------------------
   DATA DIVISION.
  *-*-*-*-*-*-*-*-
   FILE SECTION.
  *-------------------------
   WORKING-STORAGE SECTION.
  *-------------------------
   01  WS-DATA.
       05  WS-COUNTS.
           10  WS-FIRST        PIC 9(4)    VALUE ZERO.
           10  WS-LAST         PIC 9(4)    VALUE 20.
           10  WS-POSN         PIC 9(4).
       05  WS-RESULTS.
           10  WS-RS00         PIC 9(4).
           10  WS-RM05         PIC 9(4).
               88  WS-ML05                 VALUE ZERO.
           10  WS-RM03         PIC 9(4).
               88  WS-ML03                 VALUE ZERO.
       05  WS-DISPLAY-STRING   PIC X(10).
       05  WS-DISPLAY-NUM REDEFINES WS-DISPLAY-STRING.
           10  WS-FIZZBUZZ-PRI PIC X(3).
           10  WS-FIZZBUZZ-INT PIC Z(3)9.
           10  WS-FIZZBUZZ-SUI PIC X(3).
       05  WS-DISPLAY-ALPHA REDEFINES WS-DISPLAY-STRING.
           10  WS-FIZZBUZZ-STR PIC X(10).
               88  WS-IS-OTHER             VALUE "*  0000  *".
               88  WS-IS-FIZZ              VALUE "*  FIZZ  *".
               88  WS-IS-BUZZ              VALUE "*  BUZZ  *".
               88  WS-IS-FIZZBUZZ          VALUE "*FIZZBUZZ*".

  *-*-*-*-*-*-*-*-*-*-*
   PROCEDURE DIVISION.
  *-*-*-*-*-*-*-*-*-*-*
   MAIN-PROCEDURE.
        PERFORM    10000-BEGIN
        PERFORM    20000-MAIN
           VARYING WS-POSN FROM WS-FIRST BY 1
             UNTIL WS-POSN > WS-LAST
        PERFORM    30000-END
        .
        STOP RUN.
  *
   10000-BEGIN.
       DISPLAY "**********"
       .
  *
   20000-MAIN.
       PERFORM 40000-COMPUTE-FIZZBUZZ
       DISPLAY WS-DISPLAY-STRING
       .
  *
   30000-END.
       DISPLAY "**********"
       .
  *
   40000-COMPUTE-FIZZBUZZ.
       DIVIDE WS-POSN BY 5 GIVING WS-RS00 REMAINDER WS-RM05
       DIVIDE WS-POSN BY 3 GIVING WS-RS00 REMAINDER WS-RM03
       EVALUATE TRUE
           WHEN (WS-ML05 AND WS-ML03)
               SET WS-IS-FIZZBUZZ  TO TRUE
           WHEN WS-ML05
               SET WS-IS-BUZZ      TO TRUE
           WHEN WS-ML03
               SET WS-IS-FIZZ      TO TRUE
           WHEN OTHER
               SET WS-IS-OTHER     TO TRUE
               MOVE WS-POSN        TO WS-FIZZBUZZ-INT
       END-EVALUATE
       .
  *
   END PROGRAM FIZZBUZZ.

Result :

**********
*FIZZBUZZ*
*     1  *
*     2  *
*  FIZZ  *
*     4  *
*  BUZZ  *
*  FIZZ  *
*     7  *
*     8  *
*  FIZZ  *
*  BUZZ  *
*    11  *
*  FIZZ  *
*    13  *
*    14  *
*FIZZBUZZ*
*    16  *
*    17  *
*  FIZZ  *
*    19  *
*  BUZZ  *
**********
\$\endgroup\$
4
\$\begingroup\$

As I'm sure you are aware, COBOL tends to be written one way at one site, another way at another.

Often things like name prefixes, and paragraph/SECTION prefixes cannot be other than dictated. Same with case of identifiers.

I dislike WSnn- (drives people nuts trying to insert numbers so that new data can be located rationally rather than in the order of arrival in specification) and WS- (similar for the LINKAGE SECTION) but am happy with W-/L-/LS- (or nothing). Absolutely hate single-word identifiers. Even these days with forewarning of what may appear later, it is plain dumb to have them there waiting for some compiler-change to make them invalid.

Also, since COBOL has no concept of "main", it is something to avoid pretending exists.

For paragraphs, or SECTIONS, I go with two numerics. Then with each subsequent level, append an alpha, A, D, G, J, M etc (allowing for insertions), and keep all related paragraphs/SECTIONS together, physically. Your program only has one level, so it doesn't really matter what you do in that one.

Nicely laid-out. I tend to be more extreme, but the effort you've invested impresses the reader (if they are a programmer, rather than a 9-5-er).

Hate the comments.

I'd remove all the redundant code (you don't need an ENVIRONMENT DIVISION, for instance).

Ah, for these last two, they have been generated for you, so I'd suggest copying an existing model program rather than using the OpenCOBOLIDE bolierplate.

I only use VARYING with things that, well, vary. So I'd use TIMES. Describe the situation better. You have to do more typing, but in addition to the better description of the data, your code will be faster than using VARYING.

Forget ideas like using REM. COBOL has done remainders since day one, a considerable time ago, so why (I don't know the answer to this) is there a function for it? In Enterprise COBOL (IBM Mainframe) REM would return a floating-point item, then converted to what you define in your program. Wow, do we need that?

I would work on the data-names a bit, but they are better than VAR1, VAR2 (or WS13-VAR1 and WS34-VAR2).

Here's an example of how I like to format a program:

http://ibmmainframes.com/viewtopic.php?p=278927#278927

And since that is a link, here's the code:

   IDENTIFICATION DIVISION.
   PROGRAM-ID. BBHEXD.
   ENVIRONMENT DIVISION.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01  W-WHEN-COMPILED                      PIC X(8)BX(8).

   01  W-DISPLAY-HEX-MAX-UIND      USAGE IS INDEX.
   01  W-FIRST-TIME-FLAG                    PIC X VALUE "Y".
       88  W-FIRST-TIME-IN-PROGRAM          VALUE "Y".
       88  W-NOT-FIRST-TIME-FLAG            VALUE "N".

   01  W-WORK-BYTE-FOR-CALC         COMP    PIC S9(4) VALUE ZERO.
   01  FILLER REDEFINES W-WORK-BYTE-FOR-CALC.
       05  FILLER                           PIC X.
       05  W-WORK-BYTE                      PIC X.

   01  W-LEFT-HALF-BYTE-NUM         COMP    PIC S9(4).
   01  W-RIGHT-HALF-BYTE-NUM        COMP    PIC S9(4).
   01  W-BYTES-TO-OUTPUT            COMP    PIC S9(4).
   01  W-BYTES-MOVED                COMP    PIC S9(4).

   01  W-HEX-ALPHA-SUB              COMP    PIC S9(4).
   01  W-HEX-ALPHA-TABLE VALUE "ABCDEF".
       05  FILLER OCCURS 6 TIMES
            INDEXED BY W-HEX-ALPHA-IDX.
           10  W-HEX-ALPHA                  PIC X.

   01  W-DISPLAY-HEX.
       05  W-LEFT-HEX                       PIC X.
       05  W-RIGHT-HEX                      PIC X.

   01  W-HEX-TO-DISPLAY-LEFT-TABLE VALUE SPACE.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-BYTES-TO-OUTPUT
           INDEXED BY W-DISPLAY-HEX-LEFT-IND.
           10  W-HEX-TO-DISPLAY-LEFT        PIC X.
               88  W-HEX-TO-DISPLAY-LEFT-IS-EMPTY
                                            VALUE SPACE.
   01  W-HEX-TO-DISPLAY-RIGHT-TABLE VALUE SPACE.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-BYTES-TO-OUTPUT
           INDEXED BY W-DISPLAY-HEX-RIGHT-IND.
           10  W-HEX-TO-DISPLAY-RIGHT       PIC X.

   01  W-BYTE-TO-DISPLAY-TABLE VALUE SPACE.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-BYTES-TO-OUTPUT
           INDEXED BY W-DISPLAY-BYTE-IND.
           10  W-BYTE-TO-DISPLAY            PIC X.
   01  W-CONVERT-TO-HEX-FROM-DIGIT   COMP   PIC S9(4).

   01  W-CONVERTED-HEX-DIGIT                PIC X.
   01  W-CONVERTED-HEX-DIGIT-NUM
         REDEFINES W-CONVERTED-HEX-DIGIT    PIC 9.

   01  W-CONVERTED-HEX-DIGIT-NUM4           PIC 9(4).
   01  FILLER REDEFINES W-CONVERTED-HEX-DIGIT-NUM4.
       05  FILLER                           PIC XXX.
       05  W-CONVERTED-HEX-DIGIT-NUM4L      PIC 9.

   01  W-COLS-LINE.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-BYTES-TO-OUTPUT.
           10  FILLER                       PIC X.

   01  W-COLS-TABLE.
       05  FILLER                           PIC X(10) VALUE
           "----+----1".
       05  FILLER                           PIC X(10) VALUE
           "----+----2".
       05  FILLER                           PIC X(10) VALUE
           "----+----3".
       05  FILLER                           PIC X(10) VALUE
           "----+----4".
       05  FILLER                           PIC X(10) VALUE
           "----+----5".
       05  FILLER                           PIC X(10) VALUE
           "----+----6".
       05  FILLER                           PIC X(10) VALUE
           "----+----7".
       05  FILLER                           PIC X(10) VALUE
           "----+----8".
       05  FILLER                           PIC X(10) VALUE
           "----+----9".
       05  FILLER                           PIC X(10) VALUE
           "----+----0".
       05  FILLER                           PIC X(10) VALUE
           "----+----1".
       05  FILLER                           PIC X(10) VALUE
           "----+----2".
       05  FILLER                           PIC X(10) VALUE
           "----+----3".
       05  FILLER                           PIC X(02) VALUE
           "--".

   LINKAGE SECTION.

   01  L-DATA-LENGTH COMP PIC S9(8).

   01  L-BBHEXD-CONTROL-BLOCK.
       05  L-BBHEXD-CB-MAX-LINE         COMP PIC S9(4) VALUE +80.
           88  L-BBHEXD-CB-MAX-LINE-VALID    VALUE +80 +132.
       05  L-BBHEXD-CB-CHUNK-LENGTH     COMP PIC S9(4) VALUE ZERO.
           88  L-BBHEXD-CB-CHUNK-LENGTH-ZERO VALUE ZERO.
       05  FILLER                            PIC X(96) VALUE SPACE.

   01  L-DATA-PICX1-FOR-USING PIC X.

   01  L-DATA-TO-DISPLAY REDEFINES L-DATA-PICX1-FOR-USING.
       05  FILLER OCCURS 99999999 TIMES
             INDEXED BY L-DTD-BYTE-FOR-C-AND-C-IDX.
           10  L-DTD-BYTE-FOR-CONV-AND-COPY PIC X.


   PROCEDURE DIVISION USING
                                    L-DATA-PICX1-FOR-USING
                                    L-DATA-LENGTH
                                    L-BBHEXD-CONTROL-BLOCK
                                    .
   00-CONTROL SECTION.

       PERFORM                      10-FIRST-TIME
       PERFORM                      30-CNV-LK-DATA-TO-HEX-AND-DISP
       GOBACK
       .
   10-FIRST-TIME SECTION.

       IF W-FIRST-TIME-IN-PROGRAM
           PERFORM                  10A-SAY-WHO-WE-ARE
           SET W-NOT-FIRST-TIME-FLAG
                                    TO TRUE
       END-IF

       PERFORM                      10D-CHECK-OUTPUT-LENGTH
       PERFORM                      10G-SET-OUTPUT-LNGTH-AND-CHUNK
       .
   10A-SAY-WHO-WE-ARE SECTION.

       MOVE WHEN-COMPILED           TO W-WHEN-COMPILED
       DISPLAY "BBHEXD This program compiled on "
                                    W-WHEN-COMPILED
       .
   10D-CHECK-OUTPUT-LENGTH SECTION.

       IF ( NOT L-BBHEXD-CB-MAX-LINE-VALID )
           DISPLAY "BBHEXD maximum line length should be 80 or 132"
           DISPLAY "BBHEXD found>" L-BBHEXD-CB-MAX-LINE "<"
           CALL "FOODUMP"
       END-IF
       .
   10G-SET-OUTPUT-LNGTH-AND-CHUNK SECTION.

       IF L-BBHEXD-CB-CHUNK-LENGTH-ZERO
           MOVE L-BBHEXD-CB-MAX-LINE
                                    TO W-BYTES-TO-OUTPUT
       ELSE
           IF ( L-BBHEXD-CB-CHUNK-LENGTH
               GREATER THAN L-BBHEXD-CB-MAX-LINE )
               DISPLAY "BBHEXD Sorry, chunks ignored, too big"
               DISPLAY "BBHEXD Chunks>" L-BBHEXD-CB-CHUNK-LENGTH "<"
               DISPLAY "BBHEXD Maxlen>" L-BBHEXD-CB-MAX-LINE "<"
               MOVE L-BBHEXD-CB-MAX-LINE
                                    TO W-BYTES-TO-OUTPUT
           ELSE
               MOVE L-BBHEXD-CB-CHUNK-LENGTH
                                    TO W-BYTES-TO-OUTPUT
           END-IF
       END-IF

       SET W-DISPLAY-BYTE-IND       TO W-BYTES-TO-OUTPUT
       SET W-DISPLAY-HEX-MAX-UIND   TO W-DISPLAY-BYTE-IND

       IF L-DATA-LENGTH EQUAL TO ZERO
           DISPLAY "BBHEXD You want to display zero bytes?"
           DISPLAY "BBHEXD Do not expect to see them"
       END-IF
       .
   30-CNV-LK-DATA-TO-HEX-AND-DISP SECTION.

       SET W-DISPLAY-HEX-LEFT-IND
           W-DISPLAY-HEX-RIGHT-IND   
           W-DISPLAY-BYTE-IND       TO +1
       MOVE ZERO                    TO W-BYTES-MOVED

       PERFORM                      30A-BYTE-BY-BYTE-CONVERT
         VARYING                    L-DTD-BYTE-FOR-C-AND-C-IDX
         FROM                       1
         BY                         1
         UNTIL                      L-DTD-BYTE-FOR-C-AND-C-IDX
           GREATER THAN             L-DATA-LENGTH

       IF ( W-BYTES-MOVED NOT EQUAL TO ZERO )
           MOVE W-BYTES-MOVED       TO W-BYTES-TO-OUTPUT
           PERFORM                  99A-DISPLAY-PART-OF-OUTPUT
       END-IF

       .
   30A-BYTE-BY-BYTE-CONVERT SECTION.

       MOVE L-DTD-BYTE-FOR-CONV-AND-COPY
             ( L-DTD-BYTE-FOR-C-AND-C-IDX )
                                    TO W-WORK-BYTE

       DIVIDE W-WORK-BYTE-FOR-CALC  BY 16
         GIVING                     W-LEFT-HALF-BYTE-NUM
         REMAINDER                  W-RIGHT-HALF-BYTE-NUM

       MOVE W-LEFT-HALF-BYTE-NUM    TO W-CONVERT-TO-HEX-FROM-DIGIT
       PERFORM                      30AA-CONVERT-HALF-BYTE
       MOVE W-CONVERTED-HEX-DIGIT   TO W-LEFT-HEX
       MOVE W-RIGHT-HALF-BYTE-NUM   TO W-CONVERT-TO-HEX-FROM-DIGIT
       PERFORM                      30AA-CONVERT-HALF-BYTE
       MOVE W-CONVERTED-HEX-DIGIT   TO W-RIGHT-HEX

       IF ( W-DISPLAY-HEX-LEFT-IND
           GREATER THAN W-DISPLAY-HEX-MAX-UIND )
           PERFORM                  99A-DISPLAY-PART-OF-OUTPUT
           SET W-DISPLAY-HEX-LEFT-IND
               W-DISPLAY-HEX-RIGHT-IND
               W-DISPLAY-BYTE-IND
                                    TO +1
       END-IF

       MOVE W-LEFT-HEX              TO W-HEX-TO-DISPLAY-LEFT
                                        ( W-DISPLAY-HEX-LEFT-IND )
       MOVE W-RIGHT-HEX             TO W-HEX-TO-DISPLAY-RIGHT
                                        ( W-DISPLAY-HEX-RIGHT-IND )
       MOVE W-WORK-BYTE             TO W-BYTE-TO-DISPLAY
                                        ( W-DISPLAY-BYTE-IND )
       ADD +1                       TO W-BYTES-MOVED
       SET W-DISPLAY-HEX-LEFT-IND
           W-DISPLAY-HEX-RIGHT-IND
           W-DISPLAY-BYTE-IND       UP BY +1
       .
   30AA-CONVERT-HALF-BYTE SECTION.

       IF ( W-CONVERT-TO-HEX-FROM-DIGIT GREATER THAN 9 )
           SUBTRACT 9               FROM W-CONVERT-TO-HEX-FROM-DIGIT
             GIVING                 W-HEX-ALPHA-SUB     
           SET W-HEX-ALPHA-IDX      TO W-HEX-ALPHA-SUB
           MOVE W-HEX-ALPHA ( W-HEX-ALPHA-IDX )
                                    TO W-CONVERTED-HEX-DIGIT
       ELSE
           MOVE W-CONVERT-TO-HEX-FROM-DIGIT
                                    TO W-CONVERTED-HEX-DIGIT-NUM4
           MOVE W-CONVERTED-HEX-DIGIT-NUM4L
                                    TO W-CONVERTED-HEX-DIGIT-NUM
       END-IF

       .
   99A-DISPLAY-PART-OF-OUTPUT SECTION.

       MOVE W-COLS-TABLE            TO W-COLS-LINE
       DISPLAY                      W-COLS-LINE

       DISPLAY                      W-BYTE-TO-DISPLAY-TABLE
       DISPLAY                      W-HEX-TO-DISPLAY-LEFT-TABLE
       DISPLAY                      W-HEX-TO-DISPLAY-RIGHT-TABLE
       MOVE SPACE                   TO W-BYTE-TO-DISPLAY-TABLE
                                       W-HEX-TO-DISPLAY-LEFT-TABLE
                                       W-HEX-TO-DISPLAY-RIGHT-TABLE
       MOVE ZERO                    TO W-BYTES-MOVED

       . 

To demonstrate the usefulness of meaningful names, here's a version with the names rubbished:

   IDENTIFICATION DIVISION.
   PROGRAM-ID. UGLY.
   ENVIRONMENT DIVISION.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01  W-A                      PIC X(8)BX(8).

   01  W-B            USAGE IS INDEX.
   01  W-C          USAGE IS INDEX.

   01  W-D              PIC S9(9).
   01  FILLER REDEFINES W-D.
       05  FILLER                           PIC X(5).
       05  W-E         PIC 9(4).
   01  W-F PIC X VALUE "Y".
       88  W-F-Y VALUE "Y".
       88  W-F-N VALUE "N".

   01  W-G         COMP    PIC S9(4) VALUE ZERO.
   01  FILLER REDEFINES W-G.
       05  FILLER                           PIC X.
       05  W-H                      PIC X.

   01  W-I         COMP    PIC S9(4).
   01  W-J        COMP    PIC S9(4).
   01  W-K            COMP    PIC S9(4).

   01  W-L              COMP    PIC S9(4).
   01  W-M VALUE "ABCDEF".
       05  FILLER OCCURS 6 TIMES
            INDEXED BY I.
           10  W-N                  PIC X.

   01  W-O.
       05  W-P                       PIC X.
       05  W-Q                      PIC X.

   01  W-R.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-K
           INDEXED BY J.
           10  W-S        PIC X.
               88  W-S-Y
                                            VALUE SPACE.
   01  W-T.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-K
           INDEXED BY K.
           10  W-U       PIC X.

   01  W-V.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-K
           INDEXED BY L.
           10  W-W            PIC X.
   01  W-X   COMP   PIC S9(4).

   01  W-Y                PIC X.
   01  W-Z
         REDEFINES W-Y    PIC 9.

   01  W-AA           PIC 9(4).
   01  FILLER REDEFINES W-AA.
       05  FILLER                           PIC XXX.
       05  W-AB      PIC 9.

   01  W-AC.
       05  FILLER OCCURS 1 TO 132 TIMES
           DEPENDING ON W-K.
           10  FILLER                       PIC X.

   01  W-AD.
       05  FILLER                           PIC X(10) VALUE
           "----+----1".
       05  FILLER                           PIC X(10) VALUE
           "----+----2".
       05  FILLER                           PIC X(10) VALUE
           "----+----3".
       05  FILLER                           PIC X(10) VALUE
           "----+----4".
       05  FILLER                           PIC X(10) VALUE
           "----+----5".
       05  FILLER                           PIC X(10) VALUE
           "----+----6".
       05  FILLER                           PIC X(10) VALUE
           "----+----7".
       05  FILLER                           PIC X(10) VALUE
           "----+----8".
       05  FILLER                           PIC X(10) VALUE
           "----+----9".
       05  FILLER                           PIC X(10) VALUE
           "----+----0".
       05  FILLER                           PIC X(10) VALUE
           "----+----1".
       05  FILLER                           PIC X(10) VALUE
           "----+----2".
       05  FILLER                           PIC X(10) VALUE
           "----+----3".
       05  FILLER                           PIC X(02) VALUE
           "--".

   LINKAGE SECTION.

   01  L-A PIC X.

   01  L-B REDEFINES L-A.
       05  FILLER OCCURS 99999999 TIMES
             INDEXED BY M.
           10  L-C PIC X.

   01  L-D COMP PIC S9(8).

   01  L-E.
       05  L-F         COMP PIC S9(4) VALUE +80.
           88  L-F-VALID    VALUE +80 +132.
       05  L-G     COMP PIC S9(4) VALUE ZERO.
           88  L-G-Y VALUE ZERO.
       05  FILLER                            PIC X(96) VALUE SPACE.


   PROCEDURE DIVISION USING         L-A
                                    L-D
                                    L-E
                                    .
   00-A SECTION.
       IF W-F-Y
           MOVE WHEN-COMPILED       TO W-A
           DISPLAY                  "UGLY program compiled on "
                                    W-A
           SET W-F-N                TO TRUE
       END-IF

       IF ( NOT L-F-VALID )
           DISPLAY "UGLY maximum line length should be 80 or 132"
           DISPLAY "UGLY found>" L-F "<"
           CALL "FOODUMP"
       END-IF
       IF L-G-Y
           MOVE L-F                 TO W-K
       ELSE
           IF ( L-G
               GREATER THAN L-F )
               DISPLAY "UGLY Sorry, chunks ignored, too big"
               DISPLAY "UGLY Chunks>" L-G "<"
               DISPLAY "UGLY Maxlen>" L-F "<"
               MOVE L-F             TO W-K
           ELSE
               MOVE L-G             TO W-K
           END-IF
       END-IF   
       SET L                        TO W-K
       SET W-B                      TO L
       SET J
           K   
           L                       TO ZERO

       PERFORM                      00-B
         VARYING                    M
         FROM                       1
         BY                         1
         UNTIL                      M
           GREATER THAN             L-D


       IF ( NOT W-S-Y )
           SET W-C                  TO J
           MOVE W-C                 TO W-D
           MOVE W-E                 TO W-K
           PERFORM                  00-D
       END-IF

       GOBACK
       .
   00-B SECTION.

       MOVE L-C
             ( M )
                                    TO W-H

       DIVIDE W-G                   BY 16
         GIVING                     W-I
         REMAINDER                  W-J

       MOVE W-I                     TO W-X
       PERFORM                      00-C
       MOVE W-Y                     TO W-P
       MOVE W-J                     TO W-X
       PERFORM                      00-C
       MOVE W-Y                     TO W-Q

       SET J
           K
           L                        UP BY +1

       IF ( J GREATER THAN W-B )
           PERFORM                  00-D
           MOVE SPACE               TO W-R
                                       W-U
           SET J
               K
               L
                                    TO +1
       END-IF

       MOVE W-P                     TO W-S
                                        ( J )
       MOVE W-Q                     TO W-U
                                        ( K )
       MOVE W-H                     TO W-W
                                        ( L )
       .
   00-C SECTION.

       IF ( W-X GREATER THAN 9 )
           SUBTRACT 9               FROM W-X
             GIVING                 W-L     
           SET I                    TO W-L
           MOVE W-N ( I )           TO W-Y
       ELSE
           MOVE W-X                 TO W-AA
           MOVE W-AB                TO W-Z
       END-IF

       .
   00-D SECTION.

       MOVE W-AD                    TO W-AC
       DISPLAY                      W-AC

       DISPLAY                      W-V
       DISPLAY                      W-R
       DISPLAY                      W-T
       . 

Now, there are a couple of bugs in this version. Enjoy finding them, as you first have to work out, with no aid, what the program does.

Since you have GnuCOBOL, track down the discussion area at SourceForge.Net, the current home of GnuCOBOL.

\$\endgroup\$
  • \$\begingroup\$ thanks a lot. I didn't know for that "TIMES" thing. I've got to look at that. For Variable names, I've got to do better, obviously. \$\endgroup\$ – gazzz0x2z Dec 4 '15 at 21:35
2
\$\begingroup\$

Some suggestions:

  • Always use descriptive variable names. Would someone new to the code know straight away what WS-RM05 or WS-RS00 refer to? In contrast, what about remainder-by-5 or div-result?
  • Use named conditions to make things clearer. Again, if you're new, would you know what WS-ML05 refers to? All that condition does is save characters and obscure the actual condition of WS-RM05 = ZERO.
  • Write identifiers in lower (or mixed) case. All caps text is generally hard to read (see this UX StackExchange question and Butterick's Practical Typography) and in COBOL, it is worth differentiating between the numerous reserved words and identifiers to tell which is which.
  • Remove the separator comments between/around division and section headers. Different divisions contain very distinctive code and you've already preceded sections by a blank line, thereby visibly separating code in different bits of the program. The separator comments are redundant. (However, if you mix sections and paragraphs in the procedure division, it may still be worth keeping them between sections for clarity.)
  • Consider removing the WS-DATA and the WS-COUNTS groups. Neither are used directly. WS-COUNTS could be replaced by a comment. Regarding WS-DATA, (I'm assuming it contains all the data to simplify initialisation), consider marking the program as INITIAL or putting items in the local-storage section instead.
  • Define constants as constants, e.g. define WS-LAST as 01 WS-LAST CONSTANT 20. or, if you're a Micro Focus user, 78 WS-LAST VALUE 20. (You have my sympathy if you're an IBM user.)
  • Use the REM intrinsic function instead of DIVIDE ... BY ... REMAINDER ... if you only need the remainder.

The following might be more personal preference than improvements:

  • Drop the leading numbers in procedure names. This seems to be a holdover from Jackson Structured Programming, which advocated a hierarchical approach to program design. The idea of a hierarchy cannot always be applied and if it is applied, the design should be intuitive enough to require no further qualification (e.g. it is logical that an output-report procedure would call the output-control-heading procedure). If not, it should be documented in comments or in a separate file, not in identifiers.
  • Remove the WS/LS/etc. prefixes as the variable name or purpose of the program should provide sufficient information to determine what section the variable is defined in.

Applying these suggestions the 10000-BEGIN and 20000-MAIN sections would become:

begin.
    DISPLAY "**********"
    .
main.
    PERFORM compute-fizzbuzz
    DISPLAY ws-display-string
    .
\$\endgroup\$
  • \$\begingroup\$ Thanks. Agree with most. Leading number & prefixes I've found useful in bigger projects(especially when you need to analyze 800 programs in a row, don't laugh, I really did it, standard numbering was extremely useful), of course here they are overkill. Constants I didn't know, and neither REM. All Caps is a norm I've always seen used everywhere in COBOL, I'll try otherwise, but it will be tough. \$\endgroup\$ – gazzz0x2z Dec 4 '15 at 19:41
  • 2
    \$\begingroup\$ @gazzz0x2z I would love to hear what lead to you having to read 800 programs in a row. \$\endgroup\$ – Edward H Dec 4 '15 at 20:22
  • 1
    \$\begingroup\$ Read & analyze. Long story short, that shop had contract number upon 7 digits. And was already at contract number 9,500,000 and growing. So they had to rebuild everything with a 9-digits number instead. My role was to prepare the automated tests, and to check which file came from where, with what inside the datas, and what had to be compared(between old & new). Hint : Timestamps are not to be compared, for example. I also had to prepare sort parameters, and for that, understanding on surface each program making each file was important. Others made the compare tool, that ran against my params. \$\endgroup\$ – gazzz0x2z Dec 4 '15 at 21:27
  • 2
    \$\begingroup\$ I strongly disagree with the 2 "personal" preference suggestions. I found section number's very useful understanding how 2 procedures relate to each other. They do not make sense in Object Orientated programming but for procedural languages (Cobol or C), they make sense. Most Cobol Shops where will be a standard (e.g. 20000 is the main program logic and it calls 210000, 220000 etc which in tern call 211000 etc). \$\endgroup\$ – Bruce Martin Dec 4 '15 at 23:04
  • 2
    \$\begingroup\$ Again I would keep the WS (or W), it distinguishes it indicates it is in working storage (this is Cobol and variables are Global). Some sites would use WC- for constants ws- general Working storage WR- reports etc. Normally there will be a site standard \$\endgroup\$ – Bruce Martin Dec 4 '15 at 23:08
2
\$\begingroup\$

You don't need IFs and you don't need modulo division.

   IDENTIFICATION DIVISION.
   PROGRAM-ID.  fizzbuzz.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01  hooray pic x(100).
   01  fred redefines hooray.
       03  hoo occurs 100 pic 9(01).
   01  world.
       03  filler pic x(05) value spaces.
       03  i pic 9(03) value 0.
       03  filler pic x(24) value '    fizz    buzzfizzbuzz'.
   01  nurk redefines world.
       03  w occurs 4 pic x(08).
   01  orld pic 9(01).
   PROCEDURE DIVISION.
       move all '1' to hooray.
       perform varying i from 3 by 3 until i > 100
           add 1 to hoo(i).
       perform varying i from 5 by 5 until i > 100
           add 2 to hoo(i).
       perform varying i from 1 by 1 until i > 100
           move hoo(i) to orld
           display w(orld).
       stop run.
\$\endgroup\$
0
\$\begingroup\$

OK, thanks you for all your input. I did learn quite a few things. I kept the PERFORM VARYING : I still need W-Posn to vary, and if I make a PERFORM TIMES, I'll have to add an ADD 1 TO W-POSN. I don't see the purpose. And I kept my numbering standard, as there was no consensus on the topic.

I ditched the comments & unused parts, went to Not-Totally-UpperCase names(that's an experiment for me, really looks strange, but not that bad), improved the naming(hope it's clearer now, don't know what I had in mind), removed some unused groups(but not all, as some I find useful for the clarity), used constants where needed.

And I went extreme in 2 directions :

  1. I changed the algorithm, so that I didn't have to use the remainder at all(to avoid further debate upon the function REM). It's slightly longer, but I don't need any division. It's inspired from This entry for Fizzbuzz in brainfuck.
  2. I added a few reporting elements at the beginning and at the end. For the beginning, I made 2 copybooks(data definition & code) for having a standardized reporting of standard informations as a header. For the end, I did add a few counters. Not in copybook because this is supposed to change for each program.

The copybooks for the header: HeaderData.cpy

   01  H-Header-Strings.
       05  H-Line-Identity.
           10  FILLER                  PIC X(20)
                                       VALUE "Program             ".
           10  H-Program-Id            PIC X(20).
       05  H-Line-Compile.
           10  FILLER                  PIC X(20)
                                       VALUE "Compiled            ".
           10  H-Date-Compiled         PIC X(08).
           10  FILLER                  PIC X(04)
                                       VALUE " at ".
           10  H-Hour-Compiled         PIC X(08).
       05  H-Line-Execute.
           10  FILLER                  PIC X(20)
                                       VALUE "Executed            ".
           10  H-Date-Executed-mm      PIC X(02).
           10  FILLER                  PIC X(01)
                                       VALUE "/".
           10  H-Date-Executed-dd      PIC X(02).
           10  FILLER                  PIC X(01)
                                       VALUE "/".
           10  H-Date-Executed-yy      PIC X(02).
           10  FILLER                  PIC X(04)
                                       VALUE " at ".
           10  H-Hour-Executed-hh      PIC X(02).
           10  FILLER                  PIC X(01)
                                       VALUE ".".
           10  H-Hour-Executed-mn      PIC X(02).
           10  FILLER                  PIC X(01)
                                       VALUE ".".
           10  H-Hour-Executed-ss      PIC X(02).
       05  H-Line-Char-Table.
           10  FILLER                  PIC X(20)
                                       VALUE "Character Table     ".
           10  FILLER                  PIC X(07).
               88  Char-Table-ASCII    VALUE "ASCII  ".
               88  Char-Table-EBCDIC   VALUE "EBCDIC ".
               88  Char-Table-Unknown  VALUE "unknown".
           10  FILLER                  PIC X(13).
       05  H-Line-OS-xxBits.
           10  FILLER                  PIC X(20)
                                       VALUE "Turning on OS with  ".
           10  FILLER                  PIC X(07).
               88  H-OS-32-Bits        VALUE "32 Bits".
               88  H-OS-64-Bits        VALUE "64 Bits".
           10  FILLER                  PIC X(13).
   01  H-When-Compiled.
       05  H-When-Compiled-Date        PIC X(08).
       05  H-When-Compiled-Hour        PIC X(08).
   01  H-When-Executed.
       05  H-When-Executed-Date.
           10  H-When-Executed-yy      PIC 9(02).
           10  H-When-Executed-mm      PIC 9(02).
           10  H-When-Executed-dd      PIC 9(02).
       05  H-When-Executed-Time.
           10  H-When-Executed-hh      PIC 9(02).
           10  H-When-Executed-mn      PIC 9(02).
           10  H-When-Executed-ss      PIC 9(02).
           10  H-When-Executed-cc      PIC 9(02).
   01  H-Space-ASCII                   PIC X(01)
                                       VALUE X"20".
       88  H-Is-ASCII                  VALUE SPACE.
   01  H-Space-EBCDIC                  PIC X(01)
                                       VALUE X"40".
       88  H-Is-EBCDIC                 VALUE SPACE.
   01  H-Pointer                       USAGE POINTER.

HeaderDisplay.cpy

       MOVE WHEN-COMPILED          TO  H-When-Compiled
       MOVE H-When-Compiled-Date   TO  H-Date-Compiled
       MOVE H-When-Compiled-Hour   TO  H-Hour-Compiled
       ACCEPT H-When-Executed-Date FROM DATE
       MOVE H-When-Executed-yy     TO  H-Date-Executed-yy
       MOVE H-When-Executed-mm     TO  H-Date-Executed-mm
       MOVE H-When-Executed-dd     TO  H-Date-Executed-dd
       ACCEPT H-When-Executed-Time FROM TIME
       MOVE H-When-Executed-hh     TO  H-Hour-Executed-hh
       MOVE H-When-Executed-mn     TO  H-Hour-Executed-mn
       MOVE H-When-Executed-ss     TO  H-Hour-Executed-ss
       EVALUATE TRUE
           WHEN H-Is-ASCII
               SET Char-Table-ASCII
                                   TO TRUE
           WHEN H-Is-EBCDIC
               SET Char-Table-EBCDIC
                                   TO TRUE
           WHEN OTHER
               SET Char-Table-Unknown
                                   TO TRUE
       END-EVALUATE
       IF FUNCTION LENGTH(H-Pointer) EQUALS 8
           SET H-OS-64-Bits        TO TRUE
       ELSE
           SET H-OS-32-Bits        TO TRUE
       END-IF
       DISPLAY     H-Line-Identity
       DISPLAY     H-Line-Compile
       DISPLAY     H-Line-Execute
       DISPLAY     H-Line-Char-Table
       DISPLAY     H-Line-OS-xxBits
       .

The Code :

   IDENTIFICATION DIVISION.
   PROGRAM-ID. FIZZBUZZ.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01  W-First                     CONSTANT            0.
   01  W-Last                      CONSTANT            20.
   01  W-Algorithm-counters.
       05  W-Posn                  PIC 9(4)    VALUE   ZERO.
       05  W-Remainder-3           PIC 9(4)    VALUE   ZERO.
           88  W-Remainder-Fizz-3              VALUE   3.
           88  W-Remainder-Fizz-0              VALUE   ZERO.
       05  W-Remainder-5           PIC 9(4)    VALUE   ZERO.
           88  W-Remainder-Buzz-5              VALUE   5.
           88  W-Remainder-Buzz-0              VALUE   ZERO.
   01  W-Results-Counters.
       05  W-Total-Printed         PIC 9(4)    VALUE   ZERO.
       05  W-Total-Numbers         PIC 9(4)    VALUE   ZERO.
       05  W-Total-Fizz            PIC 9(4)    VALUE   ZERO.
       05  W-Total-Buzz            PIC 9(4)    VALUE   ZERO.
       05  W-Total-FizzBuzz        PIC 9(4)    VALUE   ZERO.
   01  W-Display-String            PIC X(10).
   01  W-Display-Numeric           REDEFINES W-Display-String.
       05  W-FizzBuzz-Before-Int   PIC X(3).
       05  W-FizzBuzz-Int          PIC Z(3)9.
       05  W-FizzBuzz-After-Int    PIC X(3).
   01  W-display-Alpha             REDEFINES W-Display-String.
        05  W-FizzBuzz-String      PIC X(10).
           88  W-Is-Other                      VALUE   "*  0000  *".
           88  W-Is-Fizz                       VALUE   "*  FIZZ  *".
           88  W-Is-Buzz                       VALUE   "*  BUZZ  *".
           88  W-Is-FizzBuzz                   VALUE   "*FIZZBUZZ*".
        COPY "HeaderData.cpy".

   PROCEDURE DIVISION.
   00000-Control.
        PERFORM    10000-Begin
        PERFORM    20000-Main
           VARYING W-Posn FROM W-First BY 1
             UNTIL W-Posn > W-Last
        PERFORM    30000-End
        .
        STOP RUN.
   10000-Begin.
       MOVE "FizzBuzz"             TO H-Program-Id
       COPY "HeaderDisplay.cpy".
       INITIALIZE  W-Algorithm-counters
                   W-Results-Counters
       DISPLAY "**********"
       .
   20000-Main.
       PERFORM 40000-Create-FizzBuzzDisplay
       DISPLAY W-Display-String
       .
   30000-End.
       DISPLAY "**********"
       DISPLAY "Total elements printed     : " W-Total-Printed
       DISPLAY "Total numbers printed      : " W-Total-Numbers
       DISPLAY "Total Fizz printed         : " W-Total-Fizz
       DISPLAY "Total Buzz printed         : " W-Total-Buzz
       DISPLAY "Total FizzBuzz printed     : " W-Total-FizzBuzz
       .
   40000-Create-FizzBuzzDisplay.
       IF W-Remainder-Fizz-3
           SET W-Remainder-Fizz-0  TO TRUE
       END-IF
       IF W-Remainder-Buzz-5
           SET W-Remainder-Buzz-0  TO TRUE
       END-IF
       EVALUATE TRUE
           WHEN (W-Remainder-Fizz-0 AND W-Remainder-Buzz-0)
               SET W-Is-FizzBuzz   TO TRUE
               ADD 1               TO W-Total-FizzBuzz
           WHEN W-Remainder-Buzz-0
               SET W-Is-Buzz       TO TRUE
               ADD 1               TO W-Total-Buzz
           WHEN W-Remainder-Fizz-0
               SET W-Is-Fizz       TO TRUE
               ADD 1               TO W-Total-Fizz
           WHEN OTHER
               SET W-Is-Other      TO TRUE
               MOVE W-Posn         TO W-FizzBuzz-Int
               ADD 1               TO W-Total-Numbers
       END-EVALUATE
       ADD 1                       TO  W-Total-Printed
                                       W-Remainder-3
                                       W-Remainder-5
       .
   END PROGRAM FIZZBUZZ.

The resulting log :

Program             FizzBuzz            
Compiled            12/05/15 at 22.57.08
Executed            12/05/15 at 22.57.11
Character Table     ASCII               
Turning on OS with  32 Bits             
**********
*FIZZBUZZ*
*     1  *
*     2  *
*  FIZZ  *
*     4  *
*  BUZZ  *
*  FIZZ  *
*     7  *
*     8  *
*  FIZZ  *
*  BUZZ  *
*    11  *
*  FIZZ  *
*    13  *
*    14  *
*FIZZBUZZ*
*    16  *
*    17  *
*  FIZZ  *
*    19  *
*  BUZZ  *
**********
Total elements printed     : 0021
Total numbers printed      : 0011
Total Fizz printed         : 0005
Total Buzz printed         : 0003
Total FizzBuzz printed     : 0002

Of course, it's insane for a FizzBuzz : 88 lines in the main code, with 66 + 33 lines in the copybooks, for a grand total of 187 LOC. But I've got a good starting point for new adventures, I hope.

Once again, thanks for your great entries, both. I've got to choose a "winner" answer now, and it's not easy. I'll go for Bill Woodger for its great examples - even if I'm not going to copy its style anyways. I did learn quite a few things reading his code. There is always room to grow.

\$\endgroup\$
  • 1
    \$\begingroup\$ Of course, with TIMES you need to take account of things like W-POSN yourself. That's what I meant my "more code". You're using VARYING just to do that, whereas nothing varies about the termination condition, nothing varies about the data. Starting to look very good. \$\endgroup\$ – Bill Woodger Dec 6 '15 at 9:55
-1
\$\begingroup\$

I think this is better:

IDENTIFICATION DIVISION.
PROGRAM-ID. FIZZ-BUZZ.
DATA DIVISION.
   WORKING-STORAGE SECTION.
   01 WS-STR PIC X(30).
   01 WS-CNT PIC 9(03).
   01 WS-DEVIDENT PIC 9(03).
   01 WS-REMAINDER PIC 9(03).
PROCEDURE DIVISION.
    FIRST-PARA.
    PERFORM VARYING WS-CNT FROM 1 BY 1 UNTIL WS-CNT >= 20
        INITIALIZE WS-STR
        DIVIDE WS-CNT BY 3 GIVING WS-DEVIDENT REMAINDER WS-REMAINDER
        IF WS-REMAINDER = 0
            STRING 
                WS-STR DELIMITED BY SPACE
                "FIZZ" DELIMITED BY SIZE
                INTO WS-STR
            END-STRING
        END-IF
        DIVIDE WS-CNT BY 5 GIVING WS-DEVIDENT REMAINDER WS-REMAINDER
        IF WS-REMAINDER = 0
            STRING 
                WS-STR DELIMITED BY SPACE
                "BUZZ" DELIMITED BY SIZE
                INTO WS-STR
            END-STRING
        END-IF
        IF WS-STR = SPACES
            MOVE WS-CNT TO WS-STR
        END-IF
        DISPLAY WS-STR
    END-PERFORM
STOP RUN.
\$\endgroup\$
  • 3
    \$\begingroup\$ Could you please explain why your approach is better ? \$\endgroup\$ – Billal Begueradj Jan 14 '18 at 12:40
  • \$\begingroup\$ Well, there is not "better" or "worse", there are different styles for different reasons. Your style is obviously "straight to goal". Mine was to have a comprehensive ecosystem, with not only the answer to the problem, but also all the bells & whistles to be part of a greater system. Ah, and you mess a "." before "STOP RUN." \$\endgroup\$ – gazzz0x2z Jan 15 '18 at 12:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.