Programming Pascal 6 - Scope
 


:



  1. Simple syntaxes
  2. Scope
  3. Local variables and global variables
  4. Parameters (pass by value and pass by reference)
  5. Ignored return values
  6. Inter-procedure and/or functions call
  7. Nested syntax
  8. Recursive calls
  9. Inter-referenced procedures/functions
  10. Break down problems

Pascal is a structured language. It means that the problems is divided into steps and subproblems and then is coded in a structure. This division is made through procedures and functions. Well, what the hell are procedures and functions ? It is like a subprogram that has its own begin..end. It is useful when a part of program must be performed more than once in scattered parts of the program. May be it is a bit murky, but let's take a look on its syntax :

Procedure procname;
begin
:
{ commands }
:
:
end;

begin

end.


This is the simplest form of a procedure. (Functions are discussed later) As we can see that procedures are always placed ABOVE the main begin...end. Example :

uses crt;

procedure pause;
begin
writeln('Press any key when ready ...');
readkey;
end;

begin
clrscr;
writeln('ABC');
pause; { Make a call to procedure pause }
writeln('Hey, let''s pause !');
pause; { Make a call to procedure pause }
writeln('Pause again !');
pause; { Make a call to procedure pause }
end.



Run the previous example. Look at this example :



uses crt;

begin
clrscr;
writeln('ABC');
writeln('Press any key when ready ...');
readkey;

writeln('Hey, let''s pause !');
writeln('Press any key when ready ...');
readkey;

writeln('Pause again !');
writeln('Press any key when ready ...');
readkey;
end.



It gives the same effect as the first example, isn't it ? Which is nicer ? Writing same phrases or efficiently using procedures. The first example. It is clearer and cleaner. Easy to read. Especially the lines to be replaced with procedures are very long. Procedures, and functions too, make the codes healthier and easier to read. It makes life easier in debugging the program, too.


Procedures could have its own variable, called local variable. The variable is only known inside the procedure. For example :

procedure foo;
var a : byte;
begin
a:=10;
writeln(a);
end;

begin { main begin...end block }
foo;
a:=15; { This will be an error, since a is only known inside foo }
end.



Global variable is a variable that is known throughout the program and its procedures or functions. Example :

var
b : byte;

procedure foo;
var a : byte;
begin
a:=10;
b:=15; { This is legal }
end;

begin { main begin...end block }
foo;
a:=15; { This is illegal }
b:=5; { This is perfectly legal }
end.



If a local variable is declared inside a procedure with the same name as the global variable, the global variable is overrided. For example :

uses crt;
var
a : byte; { This is the global variable }

procedure foo;
var a : byte;
begin
a:=15;
writeln('foo is ',a);
end;

begin
a:=10;
writeln(a);
foo;
writeln(a);
end.



Functions are pretty much the same, except it has a return value. It just like mathematic functions. Syntax :

function funcname : returntype;
begin
:
{ commands }
:
:
end;

begin

end.


Functions are also placed before the main begin...end. Example :

uses crt;

function yesno : boolean;
var c : char;
begin
write('Are you sure (Y/N) ? ');
repeat
c:=upcase(readkey); { Make it upcase letters }
until (c='Y') or (c='N');
writeln(c);
if c='Y' then yesno:=true else yesno:=false;
end;

var
x : boolean; { Don't be tricked, this is a LOCAL variable of main block }
begin
clrscr;
writeln('Discard all changes made');
x:=yesno;
if x=true then
writeln('Changes discarded !');
else
writeln('Changes saved !');

writeln('Quitting');
x:=yesno;
if x then
begin
writeln('Process terminated !');
halt;
end;
writeln('Quit cancelled !');
writeln('Continuing process ...');
end.



Can you see a big difference between procedures and functions ? Use function if we want to get a return value. Suppose the yesno function. It returns true if user presses y and returns false if user presses n. Yes, yes, it is pretty complex. But, try to understand. Or... try to understand this simple excerpt : (This is only the begin...end part. All above is the same as the previous one.)

begin
x:=yesno;
if x then
writeln('You answered "Yes"')
else
writeln('You answered "No"');
end.


In a well-structured program, we use global variables as minimal as possible and use the optimum amount of local variables. How can we ? Well, practice a lot ! Sometimes, we need some value to be known inside a procedure. In that case, we need parameters. We can put parameters just beside the procedure's or function's name, suppose :




procedure myproc (a:integer; b:real);
begin
:
:
end;

function abc (a,b : integer; c:byte) : longint;
begin
:
:
end;



Let's see it in 'real life' !




uses crt;

procedure makewin(x1,y1,x2,y2 : byte);
var
i,j : byte;
begin
{ top }
gotoxy(x1,y1); write(#201);
for i:=x1+1 to x2-1 do write(#205);
write(#187);

{ middle }
for i:=y1+1 to y2-1 do
begin
gotoxy(x1,i); write(#186);
for j:=x1+1 to x2-1 do write(' ');
write(#186);
end;

{ bottom }
gotoxy(x1,y2); write(#200);
for i:=x1+1 to x2-1 do write(#205);
write(#188);
end;

begin
makewin(1,1,30,8);
makewin(10,10,60,18);
end.



Simple example above tell us about making a window in a coordinate (x1, y1) to (x2, y2). With this engine, we could make many windows with just entering coordinates and the SAME code. Parameters makes us easier to customize the procedures and functions. Let's see this example :




function factorial(n : byte):longint;
var
i : byte;
result : longint;
begin
result:=1;
for i:=1 to n do
result:=result*i;
factorial:=result;
end;

var
x : byte;
begin
writeln('Enter a value : '); readln(x);
writeln(x,'! is ',factorial(x));
end.



Those previous examples are widely used in programming life. Let's look at this :




procedure foo(a : byte);
begin
writeln(a); {15}
a:=10;
writeln(a); {10}
end;

var
x : byte;
begin
x:=15;
writeln(x); {15}
foo(x);
writeln(x); {Still 15}
end.



It outputs :

15
15
10
15


At first, x is 15, then passed to procedure foo as passing parameter a. Eventhough it is legal to modify a inside foo, the value of x is unaffected anyway. This type of passing method is called PASS BY VALUE. How about this :




procedure foo(var a : byte); { See the difference ? }
begin
writeln(a); {15}
a:=10;
writeln(a); {10}
end;

var
x : byte;
begin
x:=15;
writeln(x); {15}
foo(x);
writeln(x); {10}
end.



It outputs :

15
15
10
10


If we add var before a as passing parameter, the value of x is changed whenever a is changed. This type of passing method is called PASS BY REFERENCE.


We must pass by value if the parameters are not necessarily be changed in the procedure. If the parameters need changing and the change is important to be known by the caller, use pass by reference.




procedure a;
begin
:
:
end;

procedure b;
begin
:
a; { legal }
end;

begin
:
end.



It is legal for procedure b to call procedure a. But procedure a can NEVER call procedure b. It is ILLEGAL. Function can call procedures, procedures can call functions, functions can also call other functions as long as the caller is below the target (the one which is invoked). So, you'd better arrange the procedures and functions you might have.


If the if-structure and loop structures could nest each other, can procedures and functions nest each other ? YES, IT COULD ! Example :




procedure e;   { e cannot access a, b, c, and d }
begin
:
end;

procedure a;
procedure b;
begin
c; {illegal}
e; {legal}
end;
procedure c;
begin
b; {legal}
e; {legal}
end;
begin
:
b; {legal}
c; {legal}
e; {legal}
end;

procedure d;
begin
:
b; {illegal}
c; {illegal}
a; {legal}
e; {legal}
end;

begin
:
b; {illegal}
c; {illegal}
a; {legal}
d; {legal}
e; {legal}
end.



Procedure c can call procedure b since c is below b, but procedure b cannot call procedure c. Procedure a, whose begin..end block below procedure b and procedure c, can call procedure b and c. Procedure b and c cannot be access ed by procedure d and main block. So, nested procedure can only be accessed by their brothers/sisters (among the same level of nested procedure), and their parent (the procedure who endow them). Accesses between brothers follow the rule of normal procedure calling.


Practice a lot !


How about calling ourselves ? Well, IT IS PERFECTLY LEGAL too !! Example :




procedure a;
begin
a;
end;

begin
a;
end.



But it will say error since it call itself ENDLESSLY. The method of calling oneselves is called RECURSIVE CALLS. In recursive calls, we must :


  1. Provide terminating conditions.
  2. Be extremely careful in coding.
  3. Not use too many recursive calls, say 50,000 times.
If not, you could find these symptoms :

  1. Hang / stopped responding.
  2. Computer "auto-murmur".
  3. Beeping eratically.
  4. Auto-reboot.
  5. Auto-format (no...no ! Just kidding !)
  6. Other unexpected result

Let's see a very good example of recursive calls : It's factorial. See the non-recursive method of factorial function above, then see this recursive version :





function factorial (n:byte):factorial;
begin
if n<2 then { This is the terminating condition }
factorial:=1;
else
factorial:=n*factorial(n-1); { This is the recursive part }
end;

var
x : byte;
begin
writeln('Enter a value : '); readln(x);
writeln(x,'! is ',factorial(x));
end.




If x = 5,
At first call, n = 5. Factorial:=5*factorial(4); => need second call
Second call, n = 4. Factorial:=4*factorial(3); => need third call
Third call, n = 3. Factorial:=3*factorial(2); => need fourth call
Fourth call, n = 2. Factorial:=2*factorial(1); => need fifth call
Fifth call, n = 1. Factorial:=1; => inserted back to above so
4th call becomes : Factorial:=2*1; (=2) => inserted back to above so
3rd call becomes : Factorial:=3*2; (=6) => inserted back to above so
2nd call becomes : Factorial:=4*6; (=24) => inserted back to above so
1st call becomes : Factorial:=5*24; (=120)


As you may see that factorial in recursive method is simpler than ever. Suppose you miswrite n-1 to n, the terminating condition would never be functional. So, it will loop forever ! Be careful !


Well, we come to a quite advanced matter. It is sometimes used though. Inter-referenced calls. It means that 2 procedures or functions could call each other.





procedure a;
begin
b; { illegal }
end;

procedure b;
begin
a;
end;



As you may see, calling a from b is legal, but calling b from a is illegal. It sometimes necessary for a to call b and for b to call a. The real-life problem is like context-sensitive help. Sometimes the description, when it is pointed or clicked, it call the index. But also, the index has to call its description after a topic is picked. The solution is : FORWARD CALL. Example :





procedure b; forward;

procedure a;
begin
b; { now legal }
end;

procedure b;
begin
a;
end;



Use the statement forward to make b visible to a, so a can call b. It is more dangerous than recursive call. If you are not careful, you may find the same symptoms as recursive call did. What you must do is also the same as in the recursive call. Just beware : Calling procedures uses a part of memory called stack. Reciprocal calls wastes stacked much faster than recursive calls. If you run out of stack, your program would stuck.

(c) Shilpa Sayura Foundation 2006-2017