19. Common statements
In the previous chapter I mentioned that we could finish the delete program for the account number file and then copy it and do a few modifications in order to get a zip code delete program. Obviously the latter would be a great deal less complex since we need not worry about the next number file, although we may want to create a report for the deleted records. At the same time, in producing the account file delete program we stole lines from other programs since the logic was the same for the most part.
There is just one concern in our delete zip code program. We must not delete a zip code record that is used by any account in our system. If we did wipe out that record, it would cause read errors on the zip code file, which we certainly don’t want. Thus any zip code must be thoroughly researched to verify that it is obsolete and not still being used by the system. We trust that the people who give us these zip codes to delete are on the ball. The deleting of records that shouldn’t have been erased is why there may have been difficulties – which I touched upon earlier – when we shouldn’t have problems during access after having done a successful one.
As you go from program to program there will be many similarities as well as identical lines of statements. Just think of the structure for the account record and I think you will agree that it will not change from one program to the next. If it did it meant that something was drastically awry or else we had to accommodate some new field that was added. We want the same structure in every program since we are using the same file in these different programs. When we happen to change the layout of the file, we also want to make sure that we change it in all the programs that use it. We can help ourselves out in many cases by using what is referred to as a copy member. This is some member of another library that consists of lines of code that we are copying into our program. Think of it as something that doesn’t change from program to program, at least not very often.
We create the copy member once and simply include it in the program where it is to occur by using the keyword
copy.
Before, the beginning of our program was
define account-record structure
account-number integer(9)
last-name character(18)
first-name character(15)
middle-initial character
street-address character(25)
zip-code integer(5)
balance signed decimal(6.2)
and with the copy member it becomes
copy acctmemb
where
acctmemb
is nothing more than the previous eight statements above, beginning with the
define
and this saves us some work. In this case, we have brought lines into our program from another source, namely the copy member
acctmemb.
We need that member in some library of copy code from which we can extract it when we run or compile our program. The needed lines will be present when we run our program because they have been extracted from a copy library. We should probably have a separate library for all our copy members and also another library for all our programs. This helps in keeping track of where everything is. Also, if someone else will be in charge of maintaining the programs and copy members, we can tell him where to find the stuff.
Depending on your system and its naming conventions, you may be able to create a copy library called
PROD.COPYLIB.
Since you also have need for a similar test library, that one might be conveniently called TEST.COPYLIB.
The name of the two libraries for the computer programs could be simply
PROD.SOURCELIB
and
TEST.SOURCELIB.
You could call these libraries by some other names but these above suggestions are probably easier to remember and more meaningful.
Getting back to the copy member, it has a few advantages. The obvious one is that if we change it, we won’t have to change every program that uses it. If we didn’t have that copy member and we changed the layout for the account file record, one program would have the change but other programs wouldn’t. The result might be that the other programs would abend. Even if they didn’t, someone looking at the other program without the modification to the layout might get misinformation about fields in the record since what the program has is out of date.
Using the copy member might require every other program to be recompiled since the program as it was compiled earlier would have had the old layout until we compile it again. However, that is a bit easier than adding three new fields to every program that uses that copy member. In the case of the P language, we won’t need to do anything else because the execution of our programs is dynamic. This means that when we run them, the version of the copy member as it currently exists is extracted from the copy library. In our case the process couldn’t be easier. For other systems it may not be that simple.
We can use copy members for structures, procedures or for lines in a program that are found from one program to the next, such as counters and heading layouts for reports. This could come in quite handy for some kind of date routine or file access that is used repeatedly. On the other hand, I have seen programs with one copy member after another so that it takes some effort to see what the program is doing. You really have to expand the code to get the entire program in front of you since
copy acctmemb
doesn’t really tell you what fields are in the record. That’s the disadvantage.
Another disadvantage is that you may have a long list of fields in a record when all you really cared about was one or two. These you could have gotten by simply defining the fields needed and not worrying about the others. The same could apply to a copy member that has a list of statements that are used for some group of routines, like file access. Say the copy member contains all the code for reading, writing, deleting and rewriting records to a file but you only need to read the records. You still would have all the code including lines that you didn’t need. This tends to make the program longer and the more statements in a program, the more difficult is it to decipher and maintain.
As an example of a copy member that has executable statements rather than merely the layout of a file record, consider a routine that we use repeatedly to verify that dates are valid. We will assume that all the dates are eight characters and the date should represent a valid date in the format yyyymmdd. We will have to verify that it is numeric. The copy member to do this will consist of the main procedure
check-date,
along with three others. These are the statements you see below.
check-date: if date-work >= “00000000” and <= “99991231”
if date-work not = “00000000”
perform range-check
end-if
else
date-switch = 1
end-if
range-check: if date-yyyy > 0
if date-mm > 0 and <= 12
if date-dd > 0 and <= 31
perform validity-check
else
date-switch = 2
end-if
else
date-switch = 3
end-if
else
date-switch = 4
end-if
validity-check: if work-dd > 28
if date-mm = 2
perform february-check
else
if work-mm = 4 or 6 or 9 or 11
if work-dd > 30
date-switch = 6
end-if
end-if
end-if
end-if
february-check: leap-switch = “n”
if date-yyyy(3:2) = 0 then
if mod(date-yyyy, 400) = 0
leap-switch = “y”
end-if
else
if mod(date-yyyy(3:2), 4) = 0
leap-switch = “y”
end-if
end-if
if work-dd > 29 or leap-switch = “n”
date-switch = 5
end-if
We also need the variables from the define statement
define date-work structure
date-yyyy integer(4)
date-mm integer (2)
date-dd integer (2)
define date-switch integer
define leap-switch character
to make this all work.
To get started we have to set the variable
date-switch
to 0 and
date-work
to the value of the date that we are checking. The next step is to perform
check-date.
Note that the resulting values from 1 to 6 for
date-switch
mean that the date is invalid, with the following meanings:
1 - date not numeric
2 - day out of range
3 - month out of range
4 - 0 is invalid for the year
5 - invalid day for February
6 - that month has only 30 days.
Some programs that check for valid dates stop at the range check and don’t pursue validity checks. This means that a date of June 31 is acceptable as well as February 30 but our logic won’t accept those as valid dates. Nor will it accept February 29, 2100 since that year will not be a leap year, as far as I know. That is why we have so many statements in the copy member. However, once we write it and test the logic, we can use the same copy member in any program that does date validity checking.
The first check will allow for the date to be all zeroes. Then we have
if date-yyyy > 0
to check that the zero is entered for the year. Valid years can be 1 and –1, with the latter representing 1 BC. We’ll be concerned with starting at the year 1 and going up to December 31, 9999. Someone else can take care of other dates. The next two statements relating to
date-mm
and
date-dd
check for valid months and days of the month, with
if date-mm > 0 and <= 12
looking to see that the month is between 1 and 12, inclusive. The logical operator
<=
represents less than or equal. You’ve seen it before. In the range-check procedure the variable
date-switch
could be 2, 3 or 4 depending on whether the day was out of range, the month was out of range or the year was entered as zero, respectively.
If the date passes these initial checks, we perform the procedure
validity-check.
This makes sure we don’t have dates like April 31 or February 29 if the corresponding year is not a leap year. If the day is 28 or less, we know that this is a valid date and we are done with checking. That is why the first line is
validity-check: if work-dd > 28
and we start with a check for February. If the month is 2, we do the February check, which I will get to later. Otherwise we check for the 30-day months and if we have one and the day is 31, we have an invalid date. Any other date that is not in February will pass the test and we are done. The procedure
february-check
simply verifies that a day of 29 corresponds to a leap year. If we don’t have a leap year or if the day is greater than 29,
date-switch
will be set to 5, since the date is invalid. To check for the leap year, we have two possibilities. The first is that the year could end in two zeroes, like 1900 or 2000. That year will be a leap year only if the year is perfectly divisible by 400. Thus 2000 was a leap year but 1900 and 1800 were not. I wasn’t around in the latter two cases but take my word for it. If the year doesn’t end in two zeros, it will be a leap year if the year is divisible by 4. This brings us to our next keyword,
mod.
The line
mod(date-yyyy, 400)
will be 0 if the