In a message dated 01-17-98, Michael Ferrio said to All about Does This
Program Work?
[mega snip]
MF> Could someone test this program, and possibly correct it (or at least
MF>report what their PL/I compiler says about it?) One final note: my
MF>terminal does not have the 'not' character of PL/I (ASCII decimal 172),
MF>so I had to print it by using the BASIC program
MF>100 PRINT CHR$(172)
It is CHR$(170), not CHR$(172), for the 'not' sign.
The following is a scan using the Mk. I Eyeball<tm> technology, rather than
a PL/I compiler.
MF> P1:
MF> /*** THIS PROGRAM SORTS AND SEARCHES A FILE ***/
MF> PROCEDURE OPTIONS(MAIN);
MF> DECLARE
MF> CLASS_LIM FIXED DECIMAL(7,0) INIT(25),
MF> 1 FILE_LINE
Needs a comma.
MF> 2 NAME CHARACTER(1:12),
In PL/I, character strings are not arrays. There is no lower and upper
bound, just a length. The above should be CHARACTER(12) or CHAR(12).
[Abbreviations are used at most PL/I sites as a standard.]
MF> 2 FILLER_1 CHARACTER(1) INIT(' '),
MF> 2 TEST_SCORE FIXED DECIMAL(7,0),
MF> 1 FILE_VAR
MF> 2 FILE_LINE(1:CLASS_LIM),
What does this do?
I take it you meant:
1 FILE_VAR,
2 FILE_LINE(1:CLASS_LIM),
3 NAME CHARACTER(12),
3 FILLER_1 CHARACTER(1) INIT(' '),
3 TEST_SCORE FIXED DECIMAL(7,0),
You will be better off using a dynamic allocation based on CLASS_SIZE. Let's
try:
1 FILE_VAR CTL,
2 FILE_LINE(1:CLASS_SIZE),
3 NAME CHARACTER(12),
3 TEST_SCORE FIXED DECIMAL(3,0),
I have ditched the FILLER_1 since it will prove unnecessary. Also, you are
using F(2) to read the TEST_SCORE, so I have reduced the precision to match.
MF> (J, K, CLASS_SIZE, SUM, FOUND) FIXED DECIMAL(7,0),
SUM is the name of a built-in function that will prove useful later on.
Also, BIN FIXED variables make better subscripts, so J, K, CLASS_SIZE and
FOUND should all be binary, really.
(J, K, CLASS_SIZE, FOUND) BIN FIXED(31,0),
SUM DEC FIXED(7,0),
MF> AVERAGE FIXED DECIMAL(7,0),
You will be using a decimal place for a F(5,1) print format.
AVERAGE FIXED DECIMAL(3,1),
MF> CLASS_NAME CHARACTER(1:40),
MF> SEARCH_NAME CHARACTER(1:12),
Again, proper character strings, not arrays.
MF> (MASTER_LIST, WISH_LIST) FILE INPUT,
MF> RESULTS FILE OUTPUT;
Depending on the target platform, some ENVIRONMENT options can be useful
here. Also, if you are avoiding defaults then you have to have STREAM, since
PL/I has 2 types of file: STREAM (text-based) and RECORD (binary transfer).
Also, files default to being external names. On the IBM mainframe, the
linkage editor limits external names to 8 characters maximum. You can get
around this by putting the INTERNAL attribute on the file declarations.
(MASTER_LIST, WISH_LIST) FILE STREAM INPUT INTERNAL,
RESULTS FILE STREAM OUTPUT INTERNAL;
Also, you can 'group' common attributes in declarations:
((MASTER_LIST, WISH_LIST) INPUT,
RESULTS OUTPUT) FILE STREAM INTERNAL;
All 3 have FILE STREAM INTERNAL, whereas MASTER_LIST and WISH_LIST have
INPUT and RESULTS has OUTPUT. Moreover, RESULTS appears to be a report, so
we should be more specific and use PRINT instead of just OUTPUT.
((MASTER_LIST, WISH_LIST) INPUT,
RESULTS PRINT) FILE STREAM INTERNAL;
MF> SORTA:
MF> /*** BUBBLE SORT -- ASCENDING ***/
MF> PROCEDURE(FILE_VAR, CLASS_SIZE);
MF> DECLARE
MF> (PREV, CURR) FIXED DECIMAL(7,0),
MF> TEMP_LINE FILE_LINE;
MF> DO PREV = 1 TO CLASS_SIZE - 1 BY 1;
MF> DO CURR = PREV + 1 TO CLASS_SIZE BY 1;
MF> IF FILE_VAR.NAME(PREV) < FILE_VAR.NAME(CURR) THEN GO TO LABEL_10;
You are allowed to bracket statements with DO; and END;, instead of using GO
TO around them.
MF> TEMP_LINE.NAME = FILE_VAR.NAME(PREV);
MF> TEMP_LINE.TEST_SCORE = FILE_VAR.TEST_SCORE(PREV);
MF> FILE_VAR.NAME(PREV) = FILE_VAR.NAME(CURR);
MF> FILE_VAR.TEST_SCORE(PREV) = FILE_VAR.TEST_SCORE(CURR);
MF> FILE_VAR.NAME(CURR) = TEMP_LINE.NAME;
MF> FILE_VAR.TEST_SCORE(CURR) = TEMP_LINE.TEST_SCORE;
MF> LABEL_10:
MF> END;
MF> END;
MF> RETURN;
MF> END SORTA;
MF> SEARCH:
MF> /*** BINARY SEARCH ***/
MF> PROCEDURE(FILE_VAR, SEARCH_NAME, CLASS_SIZE)
MF> RETURNS(FIXED DECIMAL(7,0));
MF> DECLARE
MF> (L, M, R, FLAG) FIXED DECIMAL(7,0);
These would be better as BIN FIXED, since DEC is rather slow for
subscripting and has some hardware vicissitudes to it when doing arithmetic.
On a 32-bit platform, try BIN FIXED(31,0).
MF> L = 1;
MF> R = CLASS_SIZE;
MF> FLAG = 0;
MF> DO WHILE L <= R & FLAG = 0;
DO WHILE(L <= R & FLAG =0);
The parentheses are required.
MF> M = FLOOR((L + R)/2.0);
FLOOR() is a floating point function. You would be better off using the
division operator. Simply:
M = (L + R)/2;
You will also note that I ditched the ".0" off the end of "2.0". The use of
a fraction does not necessarily make a literal floating point, especially in
a DEC FIXED context. In fact, 2.0 is of type DEC FIXED(2,1).
If you switch to using BIN variables and the modern style of PL/I, you can
use:
M = LOWER2(L + R, 1);
and you will have the division implemented by means of a bit shift to the
right by one place.
MF> IF SEARCH_NAME < FILE_VAR.NAME(M) THEN R = M - 1;
MF> IF SEARCH_NAME > FILE_VAR.NAME(M) THEN L = M + 1;
MF> IF SEARCH_NAME = FILE_VAR.NAME(M) THEN FLAG = 1;
These need ELSE separating them. They are mutually exclusive. Better yet,
try a 'case' statement:
SELECT;
WHEN(SEARCH_NAME < FILE_VAR.NAME(M))
R = M - 1;
WHEN(SEARCH_NAME > FILE_VAR.NAME(M))
L = M + 1;
OTHERWISE
FLAG = 1;
END;
MF> END;
MF> RETURN(FLAG *M);
MF> END SEARCH;
MF> /*** INPUT CLASS NAME AND NUMBER OF STUDENTS ***/
MF> PUT FILE(SYSPRINT) LIST('Enter class name: ');
MF> GET FILE(SYSIN) LIST(CLASS_NAME);
MF> PUT FILE(SYSPRINT) SKIP(1) LIST('Enter class size: ');
MF> GET FILE(SYSIN) LIST(CLASS_SIZE);
MF> PUT FILE(SYSPRINT) SKIP(1);
MF> IF CLASS_SIZE > CLASS_LIM THEN DO;
MF> PUT FILE(SYSPRINT) LIST('Sorry, class size limited to ');
MF> PUT FILE(SYSPRINT) EDIT (CLASS_LIM) ((F(2));
MF> HALT;
No such statement in PL/I. Try STOP; instead.
Also, you didn't declare SYSPRINT or SYSIN; you are using defaults. They'll
work though. Moreover, CLASS_LIM has lost its meaning, as we shall see right
now.
MF> END;
ALLOC FILE_VAR;
This allocates enough storage to hold CLASS_SIZE test scores.
MF> /*** READ STUDENT NAMES AND TEST SCORES ***/
MF> OPEN FILE(MASTER_LIST) INPUT;
You declared MASTER_LIST as INPUT, so an override is bad form, even when it
matches the declaration.
Furthermore, you will need a TITLE() option on the open to associate the
PL/I file stream with a file system dataset. Using the OS/2 or NT compiler,
this is the path/filename. Using the mainframe compiler, it is the DDNAME
used in the JCL for the execution step.
OPEN FILE(MASTER_LIST) TITLE('MSTRLIST');
This will use the dataset allocated with the DDNAME of MSTRLIST in your MVS
JCL.
MF> SUM = 0;
MF> J = 0;
MF> DO WHILE (4END_OF_FILE);
Where is this guy declared? It needs to be a BIT(1) to be in a Boolean
context. It also needs to be initialised first.
You will also need an ON-unit for ENDFILE(MASTER_LIST).
MF> J = J + 1;
MF> GET FILE(MASTER_LIST) EDIT(FILE_VAR.NAME(J)) (A(12));
MF> GET FILE(MASTER_LIST) EDIT(FILE_VAR.FILLER_1(J)) (A(1));
MF> GET FILE(MASTER_LIST) EDIT(FILE_VAR.TEST_SCORE(J)) (F(2));
This one is where run-time problems can abound!
You are hostage to the logical record length (LRECL in mainframe-speak) of
the MASTER_LIST file. You might care to force each read to be from column 1
of each logical record by grouping the GET and using a more controlled
format:
GET FILE(MASTER_LIST) EDIT
(FILE_VAR.NAME(J), FILE_VAR.TEST_SCORE(J))
(COL(1),A(12),X(1),F(2));
You will also note that the FILLER_1 has gone for a Burton. This is why I
ditched from the revised declaration.
MF> SUM = SUM + FILE_VAR.TEST_SCORE(J);
MF> END;
MF> CLOSE(MASTER_LIST);
You would be better off reading the file in one 'slurp' and using the
ENDFILE() event to terminate it.
ON ENDFILE(MASTER_LIST) GO TO END_OF_MASTER;
GET FILE(MASTER_LIST) EDIT
((FILE_VAR.NAME(J), FILE_VAR.TEST_SCORE(J)
DO J = 1 BY 1 TO CLASS_SIZE))
(COL(1),A(12),X(1),F(2));
END_OF_MASTER:
CLOSE FILE(MASTER_LIST);
However, since CLASS_SIZE is meant to be definitive, the ENDFILE() condition
should never occur. If it does, you have unfilled elements in
FILE_VAR.FILE_LINE -- big trouble.
Now, if you hadn't used SUM as a variable name, you could calculate the sum
of the test scores in a single statement. Pretend we called the variable
SIGMA instead of SUM.
SIGMA = SUM(FILE_VAR.FILE_LINE(*).TEST_SCORE);
MF> /*** SORT STUDENT NAMES AND PRINT ALL RECORDS ***/
MF> CALL SORTA(FILE_VAR, CLASS_SIZE);
MF> OPEN FILE(RESULTS) OUTPUT;
MF> PUT FILE(RESULTS) LIST('Class Name: ');
MF> PUT FILE(RESULTS) LIST(CLASS_NAME);
MF> PUT FILE(RESULTS) SKIP(1) LIST('Number of students: ');
MF> PUT FILE(RESULTS) EDIT(CLASS_SIZE) (F(2));
MF> PUT FILE(RESULTS) SKIP(4) LIST('Student Name Score');
List uses tab positions for output. You will have nuch finer control over
your printing if you stick with EDIT-directed PUTs.
The above would offer better 'visual fidelity' and be more efficient as a
single PUT statement.
PUT FILE(RESULTS) EDIT
('Class name: ',CLASS_NAME)
(2 A)
('Number of students: ',CLASS_SIZE)
(SKIP(1),A,F(2))
('Student Name','Score')
(A,X(8),A);
MF>
...
read more »