IPT - A Virtual Approach IPT A Virtual Approach by Peter Whitehouse
Quick Links:
 
 
Information and Intelligent Systems Social and Ethical Implications Human Computer Interaction Software and Systems Engineering eXercise Files Course Outline and Assessment A-Z of Geeky Acronyms Terrace Work Program 2004 Sillybus FAQ = Frequently Asked Questions Help
 
 

Sub-Programming

Procedures and Functions Generally

Procedures and Functions - General

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

SUB-PROGRAMS WITH PARAMETERS

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

  1. 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
  2. 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.
  3. 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

SIMPLE RECURSION

'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
 

wonko@wonko.info
©Copyright t 1992..2018+. Edition 26.150117
wonkosite
Creative Commons License
This work is licensed under a
Creative Commons Attribution-NonCommercial-ShareAlike 2.1 Australia License
.