Search code examples
cobol

COBOL Beer on the Wall Program


I'm making the "99 Bottles" program, but with user input on how many to take down. I'm very new to COBOL and I'm definitely overlooking something simple or just completely thinking about this the wrong way.

The following is what I currently have:

   IDENTIFICATION DIVISION.
   PROGRAM-ID. HW.

   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01 COUNTER                      PIC S99.
   01 BOTTLES                      PIC Z9.
   01 BOTTLES-REMAINING            PIC Z9.
   01 NUM                          PIC s9(02) VALUE 0.

   PROCEDURE DIVISION.
       PERFORM VARYING COUNTER FROM 99 BY NUM UNTIL COUNTER = 0
         DISPLAY "How many bottles would you like to take down?"
         ACCEPT NUM
         MOVE COUNTER to Bottles
         subtract NUM FROM COUNTER GIVING BOTTLES-REMAINING
         DISPLAY SPACES
         EVALUATE COUNTER
           WHEN 1
             DISPLAY " 1 bottle of beer on the wall, "
                     "  1 bottle of beer."
             DISPLAY "Take one down and pass it around, "
                     "no more bottles of beer on the wall."
           WHEN 2 Thru 99
             DISPLAY BOTTLES " bottles of beer on the wall, "
                     BOTTLES " bottles of beer."
             DISPLAY "Take one down and pass it around, "
                     BOTTLES-REMAINING
                     " bottles of beer on the wall."
         END-EVALUATE
       END-PERFORM
     GOBACK.

I need to make the NUM clause negative in the following statement (or the data division) so it will subtract from the counter:

       PERFORM VARYING COUNTER FROM 99 BY NUM UNTIL COUNTER = 0

Solution

  • I see a few issues here.

    First, and this is from admittedly faded memory, but I seem to recall that the VARYING clause required a constant value for the delta. I don't think you can use an actual changing NUM to do this.

    So your loop would be better off not using the VARYING clause and instead be something like (code here may not be syntactically correct COBOL, it's meant more to show intent and/or method):

    set counter to 99
    perform until counter = 0
        blah blah blah then change counter
    end perform
    

    Second, your little ditty doesn't make sense any more if you're allowed to remove more than one bottle at a time. The statements for the third stanza of the rhyme should be modified similarly to the bottles-left stanza:

    evaluate num
        when 1
            display "Take one down and pass it around, "
        when 2 thru 99
            display "Take ", num, " down and pass them around, "
    end evaluate
    

    And, finally, you probably want to avoid the situation where you remove more bottles than you have available (or less than one, for that matter). That can be done by silently enforcing those limits (clamping) immediately after getting the user input:

    accept num
    if num is less than one
        set num to one
    end if
    if num is greater than counter
        set num to counter
    end if
    

    You could also complain and require the user to enter a valid quantity but the easiest solution is probably just to clamp it.