![]() Previous |
![]() Contents |
![]() Next |
All are but parts of one stupendous whole.
Alexander Pope, An Essay on Man
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day : Integer; Month : Integer; Year : Integer; Result : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); -- Apply Zeller's Congruence Put (Result); end Weekday;
For the sake of simplicity Ill assume for now that the user will always type in a valid date. Lets see how Zellers Congruence works first. Heres the formula:
Day = ((26M-2)/10 + D + Y + Y/4 + C/4 - 2C) mod 7Here M is the number of the month, D is the day, Y is the last two digits of the year number and C is the century (the first two digits of the year number). Integer division is used, so that 19/4 = 4 rather than 4.75. The mod operation gives the remainder of an integer division, in this case the remainder from dividing by 7, i.e. a value between 0 and 6. Things are made slightly more complicated by the fact that the months have to be numbered starting with March as month 1; January and February are treated as months 11 and 12 of the previous year. We therefore need to adjust the month and year like this:
if Month < 3 then Year := Year - 1; -- subtract 1 from year number Month := Month + 10; -- convert 1 and 2 to 11 and 12 else Month := Month - 2; -- subtract 2 from month number end if;The result of the formula is a number between 0 and 6, where 0 means Sunday and 6 means Saturday. Lets see how this works using January 25th 1956 as an example. The value of D is 25 and C is 19. January 1956 counts as month 11 of 1955, so M is 11 and Y is 55. This gives us:
Day = ((26M-2)/10 + D + Y + Y/4 + C/4 - 2C) mod 7 = ((26*11-2)/10 + 25 + 55 + 55/4 + 19/4 - 2*19) mod 7 = (284/10 + 25 + 55 + 13 + 4 - 38) mod 7 = (28 + 25 + 55 + 13 + 4 - 38) mod 7 = 87 mod 7 = 3 (Wednesday).It will help to introduce an extra variable for the value C since its used twice in the formula above. Heres the latest version of the program:
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day : Integer; Month : Integer; Year : Integer; Century : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); if Month < 3 then Year := Year - 1; Month := Month + 10; else Month := Month - 2; end if; Century := Year / 100; -- first two digits of Year Year := Year mod 100; -- last two digits of Year Put (((26*Month - 2)/10 + Day + Year + Year/4 + Century/4 - 2*Century) mod 7); end Weekday;The Ada version of Zellers Congruence is practically identical to the original except that the single-letter variable names in the original have been replaced by longer names. Also, all multiplication operators must be specified explicitly; while the original formula had 26M in it, a direct Ada equivalent would have to be 26*M. The order of operations is the same as in ordinary algebra; multiplication and divisions are done before additions and subtractions, with parentheses being used to alter the order of evaluation where necessary.
The other thing to notice is that the value of Century is calculated after the if statement. This is because the if statement might change the value of Year; January 2000 is treated as being month 11 of 1999, so the value for Century will be 19 until March 2000. Likewise Year only gets trimmed to its last two digits after the first two digits have been extracted into Century. The order of events is quite important here.
The first thing to do is to decide what inputs it requires and what outputs it produces. There are three inputs: the day, the month and the year. There is a single output, which is a value between 0 and 6. This is enough information to produce a procedure specification:
procedure Day_Of (Day, Month, Year : in Integer; Result : out Integer);What we have here is the specification for a procedure called Day_Of (since Zeller would be a bit cryptic) which has four parameters. Three of them (Day, Month and Year) are Integer inputs, and the fourth (Result) is an Integer output. Designing a specification is always a good way to start writing a procedure, but what we actually need here is a procedure body (i.e. including the implementation details between is and end) rather than just a specification: specifications are generally only used in connection with packages. Writing the body is fairly straightforward; the necessary code can be extracted from the previous program:
procedure Day_Of (Day, Month, Year : in Integer; Result : out Integer) is M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year Result := ((26*M - 2)/10 + Day + Y + Y/4 + C/4 - 2*C) mod 7; end Day_Of;Notice that the parameter declarations look just like variable declarations, and that they can in fact be treated as if they were variables inside the procedure body. When the procedure is called, the actual values specified for the input parameters are copied into the corresponding parameter variables in the procedure. The procedure body is then executed, and it can make use of the input parameters just as if they were ordinary variables except that it cant alter them. Since procedures cant alter the value of in parameters, we need to copy Month and Year into ordinary variables that can be altered. Output parameters behave just like uninitialised variables; you have to set them to some value before you can use them. At the end of the procedure, the values of the output parameter variables will be copied into the variables which were used for the output parameters in the procedure call. In this case, the value of Result will be copied into the variable which was supplied for the Result parameter.
As well as in and out, we can specify parameters as in out meaning that they act as both inputs and outputs. Like an out parameter you have to supply a variable name for an in out parameter when you call the procedure so that the procedure has somewhere to store its output; the difference is that the original value of the variable before the procedure call is passed into the procedure as an input. Inside a procedure, an out parameter behaves like an uninitialised variable (you dont get the original value of the variable supplied when the procedure is called) but an in out parameter behaves like an initialised variable; it is initialised to the value of the variable supplied as the actual parameter when the procedure is called.
Heres how you could call the procedure to find out the day of the week for January 25th 1956 and put the result in a variable called R:
Day_Of (25, 1, 1956, R);This will copy the values 25, 1 and 1956 into the three input parameters Day, Month and Year and then execute the procedure. At the end of the procedure the value of the output parameter Result will be copied into the variable R.
You could also use the parameter names in the procedure call for the sake of readability:
Day_Of (Day => 25, Month => 1, Year => 1956, Result => R);or if you wanted to you could use a mixture of the two styles:
Day_Of (25, 1, Year => 1956, Result => R);If you mix the two styles in this way the named parameters must come after any without names. One of the advantages of using named parameters is that you can specify them in any order and the compiler will be able to arrange them into the correct order automatically:
Day_Of (Result => R, Month => 1, Day => 25, Year => 1956);
Day_Of (Day, Month, Year, R); Put (R);
We call Day_Of and put its result in a variable R. Then we have to pass R as a parameter to Put. Two statements are needed, not to mention the extra intermediate variable R. An alternative solution would be to define Day_Of as a function. A function is just like a procedure except that it evaluates to a result that can be used as part of an expression; for example, a function call could be used as the value for an input parameter in a procedure call (or a call to another function), or as part of an expression on the right hand side of an assignment statement. A function is restricted to producing a single result, and only in parameters are allowed. Functions and procedures are collectively referred to as subprograms.
Since there is a single result produced by the Day_Of procedure it would be easy to turn it into a function. This would allow us to write something like this:
Put ( Day_Of(Day, Month, Year) );where the result of the function call is used directly as the parameter to Put. This eliminates the extra statement and the extra variable.
Heres how you could rewrite the procedure specification above as a function specification:
function Day_Of (Day, Month, Year : Integer) return Integer;Instead of an out Integer parameter to store the result in, you specify return Integer after the parameter list to show that the function returns an Integer as its result. Note that you only have to say Integer instead of in Integer in the function parameter declaration, since you arent allowed out or in out parameters to functions. Only in parameters are allowed, and as a consequence you dont need to specify in.
Heres how the function would be implemented:
function Day_Of (Day, Month, Year : Integer) return Integer is M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return ((26*M - 2)/10 + Day + Y + Y/4 + C/4 - 2*C) mod 7; end Day_Of;The return statement specifies an expression whose value is the value to be returned as the result of the function. When you execute a return statement, you evaluate the expression and then immediately exit the function. For example, if you called the function like this:
I := Day_Of (X, Y, Z);the values of X, Y and Z would be copied into the parameters Day, Month and Year. The body of the function would then be executed up to the return statement; the value of the expression in the return statement would then be returned from the function and assigned to the variable I.
There is also a form of return statement for use in procedures:
return;All this does is to exit immediately from the procedure it is used in and return to the point where the procedure was called from.
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year : Integer; -- 1 function Day_Of (Day, Month, Year : Integer) return Integer is -- 2 D : Integer := Day; -- 3 M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return ((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7; end Day_Of; -- 4 begin -- main program Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); Put ( Day_Of(Day,Month,Year) ); -- 5 end Weekday;
At this point it is worth mentioning something about the declarations of the variables inside the Day_Of function. When a variable declaration is executed (or elaborated, to use the correct technical term) some space in memory is reserved for that variable and initialised if the declaration specifies an initial value. The variable exists until the end of the begin ... end block which follows it, and the space allocated to it is then reclaimed so that it can be used elsewhere if necessary. This means that when Day_Of is called at line 5 the variable D inside Day_Of is created when the declaration at line 3 is elaborated. D is then destroyed at the end of Day_Of (line 4) before returning to line 5. The region from the declaration to the end of the block it is defined in is known as the scope of the variable. Since the variable only exists inside its scope, it cannot be accessed from elsewhere (i.e. from another subprogram) since it might not exist at the time; thus D cannot be accessed from the main program since it only exists during the call to Day_Of at line 5. In other words, variables declared in a subprogram are local to that subprogram and are only accessible from inside that subprogram since this is the only time their existence is guaranteed. Parameters are treated the same way; they are effectively local variables which are created during the subprogram call and destroyed when the call is complete.
Variables in different scopes can have the same name as each other; this means that you dont have to worry about avoiding names which are used in other subprograms. Thus there is no confusion about using Day as the name of a variable at line 1 and as the name of a parameter at line 2; the variable called Day at line 1 is local to the main program while the parameter called Day at line 2 is local to Day_Of. When you refer to a name, the most local object of that name is accessed, so that the version of Day being referred to at line 3 is the parameter declared at line 2 whereas the version referred to in line 5 is the variable declared at line 1.
Each time a subprogram is called, the local variables it declares are created. This means that each variable will be reinitialised every time the subprogram is called if its declaration specifies an initial value; if not its value will be unpredictable. There is no guarantee that a particular variable will be allocated the same space in memory each time the subprogram is called, so there is no way of knowing what the contents of the variable will be unless the declaration explicitly specifies an initial value. In particular, variables do not retain their values from one subprogram call to the next. D, M and Y will be initialised every time Day_Of is called; C will always start off with an unpredictable value. If you do want to retain a value from one call to the next, you have to use a variable declared in the surrounding scope. In this case a variable declared in the main program before Day_Of could be used to hold a value from one call of Day_Of to the next since its scope would be that of the main program; since that scope includes Day_Of it would be accessible inside Day_Of but it would not be destroyed until the end of the main program was reached.
Locality of variables is useful for isolating errors; if during debugging you found that the value of C was wrong you would know that the fault lay somewhere inside Day_Of, since this is the only place where C is accessible. It would be perfectly possible to declare C in the main program instead of inside Day_Of (it would still be accessible inside Day_Of) but you would have to look at the main program as well as Day_Of if its value was found to be incorrect during debugging since its scope would be the whole main program. It would also be wasteful of memory, since space would be allocated for C even when it wasnt being used. The moral of the story is that variables should always be made as local as possible. If you want to, you can localise declarations even further than the subprogram level by using a declare block:
declare -- declarations local to the declare block begin -- statements using the local declarations end; -- declarations go out of scope hereIf you want to, you can also put an exception handler in a declare block like the one above:
declare -- declarations local to the declare block begin -- statements using the local declarations exception -- exception handlers for the statements above end; -- declarations go out of scope hereAlthough you may not have realised this, you met declare blocks for the first time at the end of the last chapter in connection with exception handling inside loops. The reason you might not have noticed is that the declaration section (everything before begin) was omitted, which is what you have to do if you dont want any declarations. If there are declarations between declare and begin, they are local to the block. They are created when their declarations are elaborated, they are destroyed at the end of the block, and they are only accessible within the block. As an example, heres a version of Day_Of where a declare block is used to localise the declaration of C:
function Day_Of (Day, Month, Year : Integer) return Integer is M : Integer := Month; Y : Integer := Year; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; declare C : Integer := Y / 100; -- 1 begin Y := Y mod 100; return ((26*M - 2)/10 + Day + Y + Y/4 + C/4 - 2*C) mod 7; end; -- 2 end Day_Of; -- 3C is not created until line 1 and destroyed at line 2, whereas M and Y are created at the start of the function and destroyed at line 3.
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year : Integer; function Day_Of (Day, Month, Year : Integer) return Integer is separate; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); Put ( Day_Of (Day,Month,Year) ); end Weekday;This is a lot clearer. The function is then put in a separate file, compiled separately and linked with the main program. We have to tell the compiler where the function came from by including the line separate (Weekday) at the beginning:
separate (Weekday) function Day_Of (Day, Month, Year : Integer) return Integer is D : Integer := Day; M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return ((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7; end Day_Of;This tells the compiler that function Day_Of is a function declared as being separate inside Weekday. The effect is identical to the original version with the function embedded in the declaration section of the main program (in particular, it is still within the scope of the main program), but the main program is now less cluttered and if you have to make any changes to Day_Of, a sensible compiler will only require you to recompile the file containing Day_Of and then relink the main program. The main program shouldnt need recompiling if it hasnt been changed. If the main program changes youll still have to recompile both the main program and Day_Of in case the changes to the main program had some effect on Day_Of (such as a change to the function declaration).
To get around these problems we could just compile Day_Of as an independent library unit in its own right, just like the procedure Hello which was used as a component of the program Hello_3 in chapter 2. When Day_Of is compiled it gets added to the library and it is then available for use as a component in a larger program. This is only possible if it doesnt rely on anything which is not a standard built-in part of the language or else provided in a package such as Text_IO that can be accessed using a with clause. In other words, it is restricted to accessing the same things as the main program is. In this case the function doesnt rely on anything external so theres no problem:
function Day_Of (Day, Month, Year : Integer) return Integer is D : Integer := Day; M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return ((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7; end Day_Of;Now we have to specify in the main program that Day_Of is a library unit that we want to refer to, so we need to name it in a with clause:
with Ada.Text_IO, Ada.Integer_Text_IO, Day_Of; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); Put ( Day_Of (Day, Month, Year) ); end Weekday;
function Valid (Day, Month, Year : Integer) return Boolean is begin if Year < 1901 or Year > 2099 or Day < 1 then return False; else case Month is when 1 | 3 | 5 | 7 | 8 | 10 | 12 => -- 31 day months return Day <= 31; when 4 | 6 | 9 | 11 => -- 30 day months return Day <= 30; when 2 => -- February; 28 or 29 days if Year mod 4 = 0 then return Day <= 29; else return Day <= 28; end if; when others => -- invalid month return False; end case; end if; end Valid;The function returns a Boolean result (True or False). It checks that the year is between 1901 and 2099 and that the day is not zero or less; it then uses a case statement to distinguish between the various lengths of month and checks that the day is not greater than the last day of the month. Note that the test for leap years (Year mod 4 = 0) is only adequate when Year is guaranteed to be between 1901 and 2099; it would be better to generalise it in case the range allowed for Year ever changes.
Each branch of the case statement returns a Boolean result, usually the result of comparing Day with the last day of the month. For example, Day <= 31 is a Boolean expression which will give a result of True if the day is 31 or less and False otherwise; the function just returns the result of the expression directly. The function can be used as the condition part of an if statement (which must evaluate to a Boolean value):
if Valid (D,M,Y) then -- do this if Valid returns True else -- do this if Valid returns False end if;This function is fairly closely related to the Day_Of function; there are probably dozens of date-related functions we could dream up. It would make sense to gather these functions together into a package rather than having a library full of functions with no apparent relationship to each other.
Another advantage of using a package is to avoid namespace pollution. Library units must have unique names, so the more procedures and functions we put in our library the more restricted the choice of names becomes. It is far easier to ensure that names are unique if collections of procedures and functions can be entered into the library under a single package name. Also, as youll see later, you can minimise the number of unique names even further by using child packages.
The first step is to generate a package specification. This needs to contain declarations for everything we want users of the package to be able to access: variables, subprograms, whatever. In the case of subprograms, we only provide specifications in the package specification; the actual code for subprograms goes into a separate package body. Heres a first stab at the specification of a package to contain the Day_Of and Valid functions:
package Dates is function Day_Of (Day, Month, Year : Integer) return Integer; function Valid (Day, Month, Year : Integer) return Boolean; end Dates;The package specification goes in a file by itself and must be compiled and added to the library. Next we produce a package body to provide the implementation of the two functions. The package body has the same name as the package specification; the only difference is that it begins with the words package body rather than just package:
package body Dates is function Day_Of (Day, Month, Year : Integer) return Integer is D : Integer := Day; M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return (((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7); end Day_Of; function Valid (Day, Month, Year : Integer) return Boolean is begin if Year < 1901 or Year > 2099 or Day < 1 then return False; else case Month is when 1 | 3 | 5 | 7 | 8 | 10 | 12 => return Day <= 31; when 4 | 6 | 9 | 11 => return Day <= 30; when 2 => if Year mod 4 = 0 then return Day <= 29; else return Day <= 28; end if; when others => return False; end case; end if; end Valid; end Dates;The package body can now be compiled and added to the library, and then were finished. Note that we dont have to provide a with clause for Dates at the start of the package body; a package body is automatically granted access to its own specification.
We can use this package in any of our programs in exactly the same way as any other package:
with Ada.Text_IO, Ada.Integer_Text_IO, Dates; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); if Dates.Valid(Day,Month,Year) then Put (Dates.Day_Of (Day, Month, Year)); else Put_Line ("Invalid date!"); end if; end Weekday;The program has to include the package in the with clause at the beginning; the declarations in the package specification are then accessible so that we can refer to the functions Dates.Day_Of and Dates.Valid. We could of course name Dates in a use clause so that Day_Of and Valid can be used without having to specify the package name as a prefix.
Note that only the package specification is accessed by a with clause; anything defined inside the package body is local to the package and hence invisible to programs which use the package. The package body might contain extra subprograms which are used to help implement the ones declared in the package specification; for example, there might be a function called Month_Length which returns the number of days in a given month:
package body Dates is -- Day_Of is accessible to users via the specification function Day_Of (Day, Month, Year : Integer) return Integer is D : Integer := Day; M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return (((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7); end Day_Of; -- This function is local to the package body and -- is not accessible via the package specification function Month_Length (Month : Integer) return Integer is begin case Month is when 2 => return 28; when 4 | 6 | 9 | 11 => return 30; when 1 | 3 | 5 | 7 | 8 | 10 | 12 => return 31; when others => return 0; end case; end Month_Length; -- Valid is accessible to users via the specification -- and is implemented using the local function Month_Length function Valid (Day, Month, Year : Integer) return Boolean is begin if Year < 1901 or Year > 2099 or Day < 1 then return False; elsif Month = 2 and Year mod 4 = 0 then return Day <= 29; else return Day <= Month_Length (Month); end if; end Valid; end Dates;Since Month_Length is not declared in the package specification, it cannot be accessed by programs which use the package; the only place it can be accessed is within the package body itself.
Note that any variables you might declare in the package specification will effectively be global variables rather than local variables; they will be created before executing the main program when the with clause for the package is elaborated, they will not be destroyed until after exiting from the main program, and they will be accessible wherever the package is accessible. Variables declared in the package body itself rather than inside a particular subprogram in the package body will also be created before the start of the program and destroyed after it finishes, although in this case they will not be accessible from outside the package body.
The package body shown above consists entirely of subprogram declarations. You are also allowed to put a series of statements in a package body in exactly the same way as you do in a subprogram (after any declarations, between begin and end):
with Ada.Text_IO; package body Dates is ... -- as above begin Put_Line ("This program uses Dates by John English"); end Dates;The statements after begin are executed when the package body is elaborated, typically at the point where the package is accessed by a with clause just before the main program is executed. They can be used to perform any initialisation that may be needed before the package is used for the first time. This is not a terribly common requirement, but one use for it might be to display a copyright notice of some sort at the start of the main program. In the example above, the message This program uses Dates by John English will be displayed whenever any main program which uses the package starts up.
Im going to do the same thing in this book. Im going to define a parent package called JE (my initials) and make every other package in the book a child of that. Here it is:
package JE is -- an empty package! end JE;This package specification is only there to act as a parent for other packages. Since theres nothing in it, theres no need for a package body. In fact, you arent allowed to provide a package body if it isnt needed (i.e. if there are no subprograms declared in the specification). All we need to do is compile the package specification and add it to the library. Now we can rewrite the previous package like this:
package JE.Dates is function Day_Of (Day, Month, Year : Integer) return Integer; function Valid (Day, Month, Year : Integer) return Boolean; end JE.Dates;All Ive done is to change the name of the package from Dates to JE.Dates. JE.Dates is effectively an extended version of the parent package JE; everything declared in JE is also part of JE.Dates (which doesnt give you anything extra in this case since JE is an empty package!). In other words, naming JE.Dates in a with clause automatically gives you access to the contents of the parent package JE.
Now the package bodys name needs changing to match:
package body JE.Dates is function Day_Of (Day, Month, Year : Integer) return Integer is begin -- as before end Day_Of; function Valid (Day, Month, Year : Integer) return Boolean is begin -- as before end Valid; end JE.Dates;and the main program needs changing to refer to JE.Dates instead of Dates:
with Ada.Text_IO, Ada.Integer_Text_IO, JE.Dates; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); if JE.Dates.Valid(Day,Month,Year) then Put (JE.Dates.Day_Of (Day, Month, Year)); else Put_Line ("Invalid date!"); end if; end Weekday;The main effect of this change is that only the name JE needs to be unique in the library; something else called Dates will not clash with JE.Dates.
It is also possible to declare subprograms as children of packages; the function Day_Of could have been declared as a child of the package JE like this:
function JE.Day_Of (Day, Month, Year : Integer) return Integer is D : Integer := Day; M : Integer := Month; Y : Integer := Year; C : Integer; begin if M < 3 then Y := Y - 1; M := M + 10; else M := M - 2; end if; C := Y / 100; -- first two digits of Year Y := Y mod 100; -- last two digits of Year return ((26*M - 2)/10 + D + Y + Y/4 + C/4 - 2*C) mod 7; end JE.Day_Of;All thats changed from the original version of the function is the name (JE.Day_Of instead of Day_Of). The main program will have to have a with clause referring to JE.Day_Of to use it:
with Ada.Text_IO, Ada.Integer_Text_IO, JE.Day_Of; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Weekday is Day, Month, Year, Century : Integer; begin Put ("Enter a date: "); Get (Day); Get (Month); Get (Year); Put (JE.Day_Of (Day, Month, Year)); end Weekday;
4.1 | The procedure Ada.Integer_Text_IO.Get skips any leading spaces and then reads
an integer, but Ada.Text_IO.Get reads a character without skipping leading
spaces. Write a procedure called Get_Non_Space which skips leading spaces and
reads the next non-space character into the variable supplied as its parameter and
write a simple program which tests that it works properly.
|
4.2 | Write a procedure which takes three integer parameters representing a date and a
fourth integer parameter representing a number of days and adds the specified
number of days to the date (so that, for example, adding 7 to December 26th 1995
will give January 2nd 1996). Add this procedure to the Dates package described in
this chapter and write a simple program which tests that it works properly.
|
4.3 | Write a function to calculate the greatest common divisor (GCD) of two integers.
You can do this using Euclids algorithm: the GCD of two values X and Y is
X if Y is zero, otherwise the process should be repeated using the values Y and X
mod Y instead of X and Y respectively (e.g. for X = 9, Y = 6, since Y isnt zero
the process is repeated with X = 6, Y = 9 mod 6 = 3; since Y still isnt zero its
repeated again with X = 3, Y = 6 mod 3 = 0, and this time Y is zero so the result
is 3). The absolute value (positive magnitude) of the result should be returned (you
can get the absolute value of a number X using the expression abs X). Write a
simple program which tests that it works properly.
|
4.4 | Many computers have an ANSI-compatible display. Write a package to do
simple screen handling for an ANSI-compatible display which contains
procedures called Clear_Screen and Move_Cursor:
procedure Clear_Screen; procedure Move_Cursor (Row, Column : in Integer);You can perform these operations by using Put to display escape sequences consisting of the escape character (which can be referred to using the name ASCII.ESC) followed by a string representing a screen management command, e.g. "[2J" to clear the screen or "[5;6H" to move the cursor to row 5, column 6 on the screen. |
![]() Previous |
![]() Contents |
![]() Next |
This file is part of
Ada 95: The Craft of Object-Oriented Programming
by John English.
Copyright © John
English 2000. All rights reserved.
Permission is given to redistribute this work for non-profit educational
use only, provided that all the constituent files are distributed without
change.