struct
, and the OCCURS keyword, which defines an array. Let's say that requirements for our toy name and address processing application have changed again, and that Canadian addresses and post codes must be supported. We'll put a record type indicator in the unused space at the end of the card, and lay out the city-state-zip storage differently depending on the record type. Remember how we left some space in the level numbers for maintenance? Here's a case where the practice comes in handy:
01 CARD-IMAGE.
03 CARD-NAME-AND-ADDRESS.
05 CARD-NAME.
07 CARD-FIRST-NAME PIC X(10).
07 CARD-LAST-NAME PIC X(10).
05 FILLER PIC X(3).
05 CARD-ADDRESS.
07 CARD-ADDRESS-LINE-1 PIC X(15).
07 CARD-ADDRESS-LINE-2 PIC X(15).
06 CARD-USA-AREA.
07 CARD-CITY PIC X(10).
07 CARD-STATE PIC X(2).
88 CARD-IS-DISTRICT VALUE "DC".
88 CARD-IS-COMMONWEALTH VALUE "PA", "KY", "VA", "MA".
07 FILLER PIC X(8).
07 CARD-ZIP-CODE PIC 9(5).
06 CARD-CANADA-AREA.
REDEFINES CARD-USA-AREA.
07 FILLER PIC X(10).
07 CARD-PROVINCE PIC X(2).
07 FILLER PIC X(6).
07 CARD-POST-CODE PIC X(7).
03 CARD-RECORD-TYPE PIC X(2).
88 CARD-IS-USA VALUE "US".
88 CARD-IS-CANADA VALUE "CA".
Granted, there is opportunity for the record type indicator to disagree with the way the storage is used: object-oriented languages do have something to offer here.
We don't have to provide a separate name for the city part of CARD-CANADA-AREA: we can use CARD-CITY to refer to characters 54 through 63, irrespective of record type.
Now, let's say that we want to print the city and state part of the card image, separated by a comma, with the trailing whitespace squeezed out, for example, "New York, NY". (The STRING statement can also be used to do this, but that's a post for another day.) We can use OCCURS to treat the characters of CARD-CITY as an array (1-based) of 10 characters, and similarly for CARD-STATE.
* * *
07 CARD-CITY.
09 CARD-CITY-CHAR PIC X(1)
OCCURS 10 TIMES.
88 CARD-CITY-CHAR-IS-SPACE
VALUE " ".
07 CARD-STATE.
88 CARD-IS-DISTRICT VALUE "DC".
88 CARD-IS-COMMONWEALTH VALUE "PA", "KY", "VA", "MA".
09 CARD-STATE-CHAR PIC X(1)
OCCURS 2 TIMES.
TIMES is another noise word that usually isn't coded. We'll need an output area and a couple of indexes:
01 OUTPUT-STRING.
03 OUTPUT-CHAR PIC X(1)
OCCURS 13.
01 IEND PIC S9(4) USAGE COMP.
01 IFROM PIC S9(4) USAGE COMP.
01 ITO PIC S9(4) USAGE COMP.
Now we're ready to write some procedural logic. PERFORM... VARYING makes a counted
for
loop. MOVE is the workhorse assignment statement: notice that the "left hand side" is actually coded on the right.
PROCEDURE DIVISION.
* * *
* Initialize result and its indexer
MOVE SPACES TO OUTPUT-STRING
MOVE ZERO TO ITO
* Scan from the end of the city area until
* a nonspace character is found
PERFORM VARYING IEND FROM 10 BY -1
UNTIL IEND < 1
OR NOT CARD-CITY-CHAR-IS-SPACE(IEND)
CONTINUE
END-PERFORM
* [Some exception-handling logic for the case
* in which the city portion is completely blank
* could be written here.]
* Copy the city, one character at a time, to the output
PERFORM VARYING IFROM FROM 1 BY 1
UNTIL IFROM > IEND
ADD 1 TO ITO
MOVE CARD-CITY-CHAR(IFROM) TO OUTPUT-CHAR(ITO)
END-PERFORM
* Copy the comma
ADD 1 TO ITO
MOVE "," TO OUTPUT-CHAR(ITO)
* Copy the state
PERFORM VARYING IFROM BY 1 BY 1
UNTIL IFROM > 2
ADD 1 TO ITO
MOVE CARD-STATE-CHAR(IFROM) TO OUTPUT-CHAR(ITO)
END-PERFORM
There's all sorts of things we could do to improve and simplify this logic. One change would be to add code to the WORKING-STORAGE SECTION to define the entire card image as an array of 80 characters. It's common that more experienced COBOL programmers write proportionally more code in the DATA DIVISION than they do in the PROCEDURE DIVISION.
A warning, once again: all of the above code is from memory, and hasn't been subjected to compilation or testing. In particular, I don't remember for certain whether 88's can be applied to group-level items as I did above with CARD-STATE.
No comments:
Post a Comment