Here is one way to write the answer:
program ex22;
var d, m, y: integer ;
suffix, month : string;
febok, thirtydaysok: boolean;
begin
writeln ('Enter a date as three separate numbers:');
repeat
writeln ('Enter the day');
readln (d);
if (d < 1) or (d > 31) then
writeln ('Invalid day. Try again');
until (d > 0) and (d <=31);
repeat
febok := true; thirtydaysok := true;
writeln ('Enter the month');
readln (m);
if (m < 1) or (m > 12) then
writeln ('Invalid month. Try again.' );
if (m=2) and (d > 28) then
begin
febok := false;
writeln ('Day too big for February.
Try again.');
end;
if (m in [4, 6, 9, 11]) and (d > 30) then
begin
thirtydaysok := false;
writeln ('Day too big for April,
June, September or November. Try again.');
end;
until (m > 0) and (m <=12) and febok and thirtydaysok;
writeln ('Enter the year');
readln (y);
if (d in [1, 21, 31]) then suffix := 'st';
if (d in [2, 22]) then suffix := 'nd';
if (d in [3, 23]) then suffix := 'rd';
if (d in [4..20]) or (d in [24..30]) then suffix := 'th';
if (m=1) then month := 'January';
if (m=2) then month := 'February';
if (m=3) then month := 'March';
if (m=4) then month := 'April';
if (m=5) then month := 'May';
if (m=6) then month := 'June';
if (m=7) then month := 'July';
if (m=8) then month := 'August';
if (m=9) then month := 'September';
if (m=10) then month := 'October';
if (m=11) then month := 'November';
if (m=12) then month := 'December';
writeln ('You entered ', d, suffix, ' ', month, ',', y);
readln;
end.
We ask the user for a day, a month and a year and first check that these are valid. The day should be in the range 1-31. The month should be in the range 1-12. If the month is February the day must be in the range 1-28 (ignoring leap years). If the month is April, June, September or November the day must be in the range 1-30. We use a Boolean variable to decide whether the day is correct for February and also for the four 30-day months. This Boolean variable is incorporated in the if statement at the end of the month section. which has four conditions.
What has been left out? Can you suggest a way to include it? Before you do that take a look at this alternative approach:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
var febok, thirtydaysok :boolean;
d,m,y:integer;
month, suffix:string;
procedure get_day;
begin
writeln ('Enter a date as three separate numbers:');
repeat
writeln ('Enter the day');
readln (d);
if (d < 1) or (d > 31) then
writeln ('Invalid d. Try again');
until (d > 0) and (d <=31);
end;
procedure get_month;
begin
repeat
febok := true; thirtydaysok := true;
writeln ('Enter the month');
readln (m);
if (m < 1) or (m > 12) then
writeln ('Invalid month. Try again.' );
if (m=2) and (day > 28) then
begin
febok := false;
writeln ('Day too big for February. Try again.');
end;
if (m in [4, 6, 9, 11]) and (day > 30) then
begin
thirtydaysok := false;
writeln ('Day too big for April, June, September or October. Try
again.');
end;
until (m > 0) and (m <=12) and febok and thirtydaysok;
end;
procedure get_year;
beg in
writeln ('Enter the year');
readln (y);
end;
procedure add_suffix ;
begin
if (d in [1, 21, 31]) then suffix := 'st';
if (d in [2, 22]) then suffix := 'nd';
if (d in [3, 23]) then suffix := 'rd';
if (d in [4..20]) or (d in [24..30]) then suffix := 'th';
end;
procedure match_month;
begin
if (m=1) then month := 'January';
if (m=2) then month := 'February';
if (m=3) then month := 'March';
if (m=4) then month := 'April';
if (m=5) then month := 'May';
if (m=6) then month := 'June';
if (m=7) then month := 'July';
if (m=8) then month := 'August';
if (m=9) then month := 'September';
if (m=10) then month := 'October';
if (m=11) then month := 'November';
if (m=12) then month := 'December';
end;
procedure display;
begin
writeln ('The date is ', d, suffix, ' ', month, ', ', y);
end;
begin //main program
get_day; //value of d from procedure get_month
get_month;
get_year;
add_suffix;
match_month;
display;
readln;
end.
Note that the procedure declarations come after const and var. Procedures can have their own const and var declarations and even their own sub-procedures and functions. Note also the use procedure 'display', which only has one line. Having a separate procedure for output makes more sense when the output is more complex so this is a good habit to get into now.
The use of procedures breaks the problem into smaller sections or modules so the programmer can concentrate on one section at a time. The program can be built up gradually and modules or procedures can be tested separately. When all the modules are complete the program can be tested as a whole: integration testing.
It should be easier to add the missing code to this version of the program, as part of an existing procedure or as a new one.
This version of the program adds parameters to the procedure headings and calls. This creates local variables within the procedures. The var parameter mechanism updates the global variable by creating a local variable and copying this into the global one. This makes the procedures more self-contained as they now manipulate their own variables; local variables such as parameters are destroyed when the procedure finishes. Notice how one procedure returns a value to the main section of the program ready for processing by another one.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
var febok, thirtydaysok :boolean;
d,m,y:integer;
month, suffix:string;
procedure get_day(var day : integer);
begin
writeln ('Enter a date as three separate numbers:');
repeat
writeln ('Enter the day');
readln (day);
if (day < 1) or (day > 31) then
writeln ('Invalid day. Try again');
until (day > 0) and (day <=31);
end;
procedure get_month(var month:integer; day:integer);
begin
repeat
febok := true; thirtydaysok := true;
writeln ('Enter the month');
readln (month);
if (month < 1) or (month > 12) then
writeln ('Invalid month. Try again.' );
if (month=2) and (day > 28) then
begin
febok := false;
writeln ('Day too big for February. Try again.');
end;
if (month in [4, 6, 9, 11]) and (day > 30) then
begin
thirtydaysok := false;
writeln ('Day too big for April, June, September or October. Try
again.');
end;
until (month > 0) and (month <=12) and febok and thirtydaysok;
end;
procedure get_year(var y:integer);
beg in
writeln ('Enter the year');
readln (y);
end;
procedure add_suffix (day:integer; var suffix:string);
begin
if (day in [1, 21, 31]) then suffix := 'st';
if (day in [2, 22]) then suffix := 'nd';
if (day in [3, 23]) then suffix := 'rd';
if (day in [4..20]) or (d in [24..30]) then suffix := 'th';
end;
procedure find_month(m:integer; var month:string);
begin
if (m=1) then month := 'January';
if (m=2) then month := 'February';
if (m=3) then month := 'March';
if (m=4) then month := 'April';
if (m=5) then month := 'May';
if (m=6) then month := 'June';
if (m=7) then month := 'July';
if (m=8) then month := 'August';
if (m=9) then month := 'September';
if (m=10) then month := 'October';
if (m=11) then month := 'November';
if (m=12) then month := 'December';
end;
procedure display (day, year: integer; suffix, month:string);
begin
writeln ('The date is ', day, suffix, ' ', month, ', ', year);
end;
begin //main program
get_day (d); //value of d from procedure get_month
get_month (m,d); //can call procedures in any order
get_year (y);
add_suffix (d, y, suffix, month);
find_month (m, month);
display(d,y, suffix, month);
readln;
end.
Note the use of parameters in this example. In the main program section the actual parameters may or may not have values to pass to the procedures: most of them here do not: can you see which parameters are passing data to the procedures?
Parameters in procedure headers are called formal parameters. Formal parameters include the name and the type of the variables being passed. One reason for using parameters is so that the procedures can be generalised, that is they can process a range of values.
There are two types of formal parameters used here. Where a parameter appears as a name and a type only it is a 'call by value', that is the value of the data is passed from the procedure call using an actual value. The value passed to the procedure is processed in some way by the procedure but is not passed back to the point from which it was called.
Where a parameter appears with the word 'var' in front it is a 'call by reference' and the value will be returned to the point from which the procedure was called. In this case the parameter in the procedure call (the actual parameter) may be empty and even if it isn't its value when passed will be changed by the procedure when it returns it (otherwise there is no point in all this palaver!).
A call-by-reference does not pass a value to the procedure but rather a pointer to where the variable is stored. The variable is stored outside the procedure so technically speaking it is a global variable rather than a local one. Because it is stored outside the procedure the procedure can write a value into it that the main program can use as well. Remember that local variables in a procedure are destroyed when the procedure ends but a variable stored outside the procedure will be unaffected so other parts of the program can make use of it. The actual parameter in the procedure call will probably be a global variable so the procedure will use this as the var parameter. As a matter of interest arrays are always passed to procedures by reference by default: it would not be efficient to pass copies of large chunks of memory into procedures so the calls are by reference to a single copy of the array.
Most Pascal programmers prefer to use procedures, parameters and local variables rather than global variables. This is considered a cleaner and superior way of programming.
get_day (d) calls the procedure get_day with an empty value; the value will be filled by the var parameter day, which is returned by the procedure. If we try to call get_day without a parameter there will be an error because the parameters do not match. If we remove the var from the get_day procedure header the program will work but no value is passed back from the procedure and so d has no value and the date appears as 0.
get_month (m,d) calls the procedure get_month with two parameters, one a call-by-reference (m) and the other a call-by-value (d). d acquired a value in the call to get_day. The get_month procedure header thus has month as a var parameter (returned) and day as an ordinary parameter (not returned, it doesn't need to be).
Note that the parameters in the header are like variable declarations. Procedures can have their own var section but the parameters themselves are local variables. Local variables only exist while a procedure is running and they cannot be seen or accessed from outside the procedure. Parameters provide a way to get data in and out of procedures. When get_day is called the parameter is d but the declaration in the header is 'day': a local variable. The names 'day' and 'month' are used in more than one procedure but they do not interfere with each other because they are confined to the procedures where they are declared. Thus get_month declares 'month' as an integer while find_month declares month as a string but these differing declarations do not clash.
You should find the code using procedures easier to understand because it is broken into modules with names that signify their purpose and it uses parameters with meaningful names. You should also find it easier to write longer programs using procedures because they support the technique of modularisation, breaking big problems into a number of smaller ones and tackling each of those in turn. Procedures also have the advantage that they can be used repeatedly without having to write the code, for example a data entry procedure can be called whenever it is required. Libraries of procedures to perform standard tasks can be built up and inserted or adapted to the needs of a new program.
Procedures provide a level of abstraction in program development. You can look at the detail of a procedure (treat it as a 'white box'), or look only at the fact that it is used at a certain point without worrying about the details of how it works. Different programmers in a team might work on different procedures so they work at one level of abstraction while a team leader puts the modules together, sees the bigger picture and works at a different level of abstraction. Levels of abstraction are important in defining the contribution of computer science to general intellectual progress: computation thinking.
Functions provide a further mechanism for modularisation and abstraction in Pascal.
Now you can write a procedure that will perform the missing test.