Q |
Students should attempt to write a program that efficiently prints
all prime numbers up to a supplied number (primes are numbers that
have no other factors other than 1 and themselves). There are many
ways to accomplish this task - each of varying efficiency. |
By now, students should have come to some sort of 'internal' agreement
as to how they plan their programs - tasks will begin to get complex,
leaping straight into Pascal is most unwise.
In most computer languages it is possible to define ACTIONS (called
sub-programs or procedures), which can be called when needed. These
modules can be used as many times as you want in a program -
define them once, use them over and over.
Procedures are 'called' into action by mentioning their name - this
usually is a statement in itself. It is possible to write modules that
can be fed values at runtime (the values you feed are termed
parameters) as part of the call. Procedures that do not reequire parameters
are more primitive but can be just as useful.
A variation of a procedure is called a Function - these are typically
part of a statement (ie. embedded in an expression)
Parameter-less Processes
eg1: procedure fiddleabout;
:
{$R *DFM}
var a,b : integer;
Procedure ExchangeThem;
{exchanges two variables' values}
var t : integer;
begin
t := b;
b := a;
a := t
end;
Procedure TForm1.fiddleabout;
begin
a:= 5;
b:= 30;
ExchangeThem;
showmessage('a = '+inttostr(a) +' b = ' + inttostr(b));
if a > b
then ExchangeThem;
showmessage('a = '+inttostr(a) +' b = ' + inttostr(b));
end;
In the above example, a and b are GLOBAL variables
- they are owned by the main unit (they are declared under the {$R *.DFM} compiler directive making them visible to all processes in this unit.
Although the sub-program (ExchangeThem)
can see and use these global variables, it is generally NOT a good idea
to do this due to the many and varied side-effects that can result.
You will notice that ExchangeThem has it's own var section that
introdices t.t is a LOCAL variable - known only
to ExchangeThem - the main program cannot see nor access it
You will also notice ExchangeThem is called twice in
this program.
eg2: procedure initialize;
procedure calcordercost;
:
Procedure Tform1.initialize;
{set up for a new order}
begin
label1.caption :='MUCKDONALDS';
label2.caption :='McChunder Burger';
numburgers := 0;
numfries := 0;
numshakes := 0;
{....etc}
end {menu}
Procedure Tform1.calcordercost;
{works out the cost of an order}
var temp : real;
begin
temp := numburgers*2.50 + numfries*1.25 + numshakes*2.10;
ordertotal.caption := floattostr(temp)
end;
Good programs have MODULES that break the task into smaller parts,
making each of the parts independent (self contained). This simple action
can vastly improve your program, making it easier to read and maintain
(debug and add to)
eg3: procedure displayanswer;
:
{$R *.DFM}
var j,k : real;
function OneOnK : real;
begin
OneOnK := 1/k
end;
procedure TForm1.displayanswer
begin
j := 5;
k := j;
j := j * OneOnK;
showmessage('j = '+ inttostr(j) +' k = '+ inttostr(k))
end;
This function preys on GLOBAL variables - this is unwise (and generally
unheard of). You will notice the function is used in a different way
to the procedure. Whereas a procedure call is a statement, a function
call must be part of a statement
Good programmers aim to separate the code that tells the machine HOW
to perform a task (some action or process) from the actual request to
perform the task (ie. the 'site' of the call)
Very Important:
- always ensure that the function/procedure can be executed if called.
- make sure that your functions can return values in each instance
that they are called.
- each function has a specific type
General Sub-Programs
If you want to supply your procedure with a value to use in it's execution,
or you want it to safely use variables owned by the main program,
you should use parameters in your procedure definiition.
You either want your procedure to be handed a value to use (pass
by value) or you want to hand your procedure a variable to manipulate
(pass by reference).
Pass By Value - creating a 'one way link'
to reliably hand values to a procedure without it preying on global
variables.
eg1. Procedure TForm1.CountTo(num : integer); {formal parameter}
{counts to the number supplied as num}
var loop : integer;
temp : string
begin
temp := '';
for loop := 1 to num do
temp := temp + inttostr(loop) + ' ';
showmessage(temp)
end; {CountTo}
could be called in another process as:
CountTo(5);
eg2. TForm1.Function Factorial(num : integer) : integer; {formal parameter}
{returns the factorial, num!, as num*num-i*num-2*...*1}
var loop : integer;
answer : integer;
begin
answer := 1;
for loop := 1 to num do
answer := answer * loop;
Factorial := answer
end; {Factorial}
could be called in another process as:
showmessage(inttostr(Factorial(6)+Factorial(3)));
With Pass By Value, calls to the sub-program hand a value, or values,
to be used locally.
eg3. Function EuclideanDist(x1,y1,x2,y2:integer) : real;
{calculates the distance between two points using
distance formula, and returns it as a real }
begin
EuclideanDist := sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
end; {euclidean distance}
could be called in another process as:
showmessage(floattostr(EuclideanDist(3,8,7,5)));
which will return and output the distance between the points (3,8)
and (7,5).
Pass By Reference - where you provide a safe
means of communication back to the body, by temporarily allowing a local
variable to share the memory address of a another global variable.
eg4: Procedure Factorial(num : integer; var answer : integer);
{evaluates the factorial of num and returns it in answer}
var loop : integer;
begin
answer := 1;
for loop := 1 to num do
answer := answer * loop;
end; {Factorial}
could be called in the main program as:
Factorial(thing,result);
showmessage('The factorial of' + inttostr(thing) +' = '+inttostr(result));
Result and answer, at the time of the call temporarily
share the same memory cell - what happens to answer in the procedure,
also happens to result. When the procedure terminates, the resting
value of answer remains in the memory cell known as result.
eg5: Procedure WhatASCII (ch : char ; var x : integer);
{returns to x the ASCII value of ch}
begin
x := ord(ch)
end;
could be called in the main program as:
WhatASCII('Q',num);
where num and x temporarily share the same memory location,
and so after the procedure call has terminated, the result of the procedure
is stored in num
Sending a value to a sub-program = Pass by Value, specifying a variable
in a call and receiving a value back for it = Pass By Reference
eg6: Procedure Exchange(var a,b : integer);
{swaps two supplied variables and returns them swapped}
var t : integer;
begin
t := a; a:= b; b:= t
end;
Procedure Swapem(a,b : integer);
{swaps two supplied variables locally only}
var t : integer;
begin
t := a; a:= b; b:= t
end;
Begin
x := 1; y := 42;
Swapem(x, y); showmessage(inttostr(x)+' '+inttostr(y)); {no effect}
Exchange(x, y); showmessage(inttostr(x)+' '+inttostr(y)); {swapped}
Pass by value can send expressions (ie. any expression that can be
evaluated into a single value of the same type as the accepting parameter)
eg: WhatAscii(char(ord(x)*42-b), answer);
Pass by reference can only send variable names (or pointers to memory
locations)
PROCEDURAL PROBLEMS
- Side Effects
- A procedure inside another procedure may alter values defined in the ancestor.
- This causes problems that are almost impossible to locate
- beware of procedures that re-assign a global variable unexpectedly
- good programs will not have such side effects
- Environmental Dependence
- Most of the procedures encountered so far are ENVIRONMENTALLY
DEPENDENT - that is they use global variables in the host program.
This renders them NOT PORTABLE to other programs unless the
same environmental conditions exist.
- A programmers aim is to build up a 'personal library' of environmentally
independent, side-effect free procedures that can be imported
into what ever program is currently being written if needed.
- Identical Identifiers
- Syntactically, local variables (in procedures and functions)
are allowed to be named the same as global variables. Sub-program
local variables are stored in different places to globals. (control
is abandoned by the body and so locals take over anyway)
- BUT readability should prevent us from doing this unless unavoidable.
Parameterised Sub-Programs
'a contradiction in terms'
recursion(re-ker-shun) v. - see recursion
please consider:
Procedure TForm1.CountDownFrom (number:integer);
{performs a countdown to blastoff recursively}
begin
if number > 0
then
begin
showmessage(inttostr(number));
CountDownFrom(number-1)
end
else
showmessage('Blastoff')
end;
Procedure TForm1.button1click(sender :TObject);
begin
CountDownFrom(5)
end.
wind-in going to a new call without completing the previous
one
wind-back completing a call then moving back to work on a previously
called but as yet un-completed one.
please consider:
Function TForm1.pow(x, y : integer) : integer;
{returns x to the power of y recursively}
begin
if y = 0
then pow := 1
else pow := x * pow(x,y-1)
end;
Procedure TForm1.button1click(sender :TObject);
begin
showmessage(inttostr(pow(2,4)))
end;
Before you get too excited by recursion, be warned that efficiency
and memory may prohibit a recursive solution - the number of calls
within calls may cause the program to run out of memory attempting
to complete the task, or at least make it so inefficient that you
would not use it. There are, however some problems that there is no
other solution except for a recursive one (fortunately these are rare).
Recursive
Sub-Programs