Procedures and Functions
Procedures and Functions, and Scope
Java Methods
- Java has 2 kinds of methods
- What are they?
-
-
Java Methods
- Java has 2 kinds of methods
- What are they?
- void - return type is void (ie no return type )
- non-void - return type is non-void (eg int)
- Ada equivalents:
- void: Procedure
- non-void: Function
What's in a Name - Informally
- Method means void or non-void method
- Procedure means procedure or function
- Routine or subroutine means method, procedure, or function
Example Procedure and Function - Nested!
- Example - a procedure that prints a table of factorials:
procedure demo is
function factorial(n: Natural) return Natural is
ans: Natural;
begin
if n = 0 then
ans := 1;
else
ans := n * factorial(n - 1);
end if;
return ans;
end factorial;
procedure print_table(start, stop: Natural) is
begin
for i in start .. stop loop
put(i);
put(factorial(i));
new_line;
end loop;
end print_table;
first, last: Natural;
begin
get(first);
get(last)
print_table(first, last);
end demo;
Procedure print_table
and function factorial
are nested within top-level procedure demo
- Top-level = not nested in anything
- One top-level procedure per file
print_table
and factorial
are declared within the declaration section of demo
Structure of print_table and demo are the same
What is the main program and where does execution start?
Order of Declarations: Define Before Use
- print_table calls factorial: therefore Factorial must be declared above print_table
- Different from Java
- All class members (ie methods and fields visible throughout class)
- Question: But what about mutual recursion (ie a calls b and b calls a): See below
Scope
- Scope: where name is visible:
- Java scope rule:
- Fields and methods: throughout class
- Local variables: from declaration to enclosing brace
- Ada scope rule:
- Procedures and functions: From declaration to corresponding end statement
- Variables: From declaration to corresponding end statement
- In example above, what are the scopes of:
- factorial, print_table
- n, start, stop
- first, last
- ans
Local and Global Variables
- Local variable: Declared in a routine
- What are the locals in the above?
- Global variables: Visible inside a routine, but not declared in it
- Any global variables in the above?
- How could we create some? Don't do it!
Global Variables
- A declaration is global to a procedure if it is declared
outside that procedure and visible inside that procedure
- Thus, a variable that is declared above a procedure is known as
global variable (since it is global to that procedure)
- Example global variable: Don't do this:
procedure demo is
g: Integer; -- What is the scope of g? Don't do this!
procedure put_answer(ans: Integer) is
begin
put("g = " & g'img); -- Don't use globals
put("ans = " & ans'img);
end mpg;
begin -- demo
get(g);
put_answer (g * g);
end demo;
In this course we don't use global variables
- Except, perhaps, in very rare cases
- See below for why
Java Locals and Globals
- Java locals - very similar
- Java globals - different
Functions and Strong Typing
- Type of returned value must match declaration
function half_it (n : Integer) return Float is
half : Integer := n / 2;
begin
return half; -- Compile error
Return type must match use:
function half_it (n : Natural) return Float is
begin
return float(n) / 2.0;
end half_it;
half: Integer;
begin
half := half_it(10); -- Compile error: Expected Int found Float
Function Calls as Arguments
- Question: Can you have a function call as an argument to a function call?
- Answer: Sure
- Example:
put(reverse(to_upper(name)));
- compare to java:
System.out.print(name.toUpper().reverse());
Expression Functions
- Expression functions provide a light weight syntax for
functions that simply evaluate an expression
- Example:
function cube(n: Integer) return Integer is
(n * n * n);
function fac(n: Natural) return Natural is
(if n = 0 then 1 else n * fac(n - 1));
x: Integer := 11;
begin
put( cube(x) );
put( fac(x) );
No begin, return, end
Conditional expression evaluates to 1
or n*fac(n-1)
Terminology Revisited
- Procedure
- Function
- Method - void or non-void
- Subroutine - procedure or function
- Routine - procedure or function
- Formal parameter
- Actual Parameter
Parameter Modes
Parameter Modes and Java
- Nothing comparable in Java
- But to what mode are final parameters similar?
- In Java, parameters can only be used to send information into a method
- In Java, parameters can not be used to send information back from a method
- Although parameters that are objects can be changed
Parameters Modes and Information Flow
- Parameter modes allow the direction of information flow to be specified in the procedure declaration
- Parameters can be used to send information in
- Parameters can be used to send information out
- Parameters can be used to send information in and out
Parameter Modes - In and Out Modes
- Parameters have three possible modes:
procedure one(p1: in integer) is ...
procedure two(p2: out integer) is ...
procedure thr(p3: in out integer) is ...
Modes allow programmer to specify direction of
information flow between a called procedure and its
caller
- In: from caller in to called
- Out: from called out to caller
- In Out: from caller in to called and back out to caller
Default mode: in
procedure one(p1: integer) is ...
Example with In Mode and Out Mode
- Example - a procedure that calculates and prints:
- sum of 1 .. n
- sum of squares for 1 .. n
procedure demo is
procedure calcs(n: in Natural; s, ssq: out Natural) is
begin
s := 0;
ssq := 0;
for i in 1 .. n loop
s := s + n;
ssq := ssq + n ** 2;
end loop;
end calcs;
x, sumUpToX, sumSqUpToX: Natural;
begin
get(x);
calcs(x, sumUpToX, sumSqUpToX);
put(sumUpToX);
put(sumSqUpToX);
end demo;
Conceptual information flow:
- actual parameter x copied into formal parameter n
- s and ssq calculated using formal param n
- values of formal params s and ssq are copied into actual params sumUpToN and sumSqUpToN
Conceptual information flow in Java:
- actual parameter x copied into formal parameter n
Another example: use procedures and functions to sum and count positives, etc.:
sumcountprocs.adb
and
prettified
- [This example uses arrays, which we haven't covered yet, but
it should still be understandable]
Example with In Out Mode Parameters
- In Out mode: pass information from caller to called and then back to caller
- Simple Example:
procedure doubleMyParam(x: in out Integer) is
begin
x := x * 2;
end doubleMyParam;
...
i := 3;
doubleMyParam(i);
put(i); -- 6
Example - keep a running sum of i and i**2:
procedure demo is
procedure calcs(n: Natural; s, ssq: in out Natural) is
begin
s := s + n;
ssq := ssq + n ** 2;
end calcs;
x, sumUpToX, sumSqUpToX: Natural;
begin
get(x);
sumUpToX := 0; -- Statement a
sumSqUpToX := 0; -- Statement b
for k in 1 .. x loop
calcs(k, sumUpToX, sumSqUpToX);
end loop;
put(sumUpToX);
put(sumSqUpToX);
end demo;
What happens if we move Statements a and b to calcs?
Why Parameter Modes
- Increased flexibility:
- Return multiple values from a procedure
- Example: procedure sum(i: in Natural; sum, sumsq: out natural)
- Better error checking
- Allow/require programmer to specify information about how a parameter is to be used
- Provides redundancy: Mode must agree with use
- Compiler verifies that the mode is consistent with the code
- Modes are a contract between caller and called
- In: caller must provide a value for the called routine
- Out: called must return a value for the caller routine
- In Out: caller must provide a value to the called and changes
are made by the called routine
Modes: Initialization, Constants, Modification
- In mode:
- Initialized prior to call
- Constant within call!
- Out mode:
- Value before call is ignored
- Modified (ie assigned a new value) within call
- In Out mode:
- Actual parameter is assumed to have a value before call is made
- Value of actual is modified within call
Parameter Modes Improve Error Detection
- What kinds of parameter mode errors cause a compiler error or warning:
- Attempting to assign a value to an in mode parameter
- Attempting to change an in mode parameter by calling a procedure
- In mode parameters are treated like constants in the procedure:
procedure foo1(i: in Integer) is
begin
i := 3; -- error: Assignment to in mode parameter not allowed
Out mode parameters are uninitialized on entering the procedure:
procedure foo2(o: out Integer) is
begin
put(o); -- Warning: Formal parameter "o" read but never assigned
-- warning: "o" is used uninitialized in this function
-- gnatmake -gnatwall foo2.adb: gives maximum warnings
Out mode parameters must have variables as actual parameters
procedure foo3(o: out Integer) is
begin
o := 3;
end foo4;
procedure foo4(i: in Integer) is
c: constant Integer := 5;
x: Integer;
begin
foo3(x); -- Okay
foo3(2); -- error: Actual for "o" must be a variable
foo3(c); -- error
foo3(i); -- error
Parameter Mode vs Parameter Passing Mechanism
- Question from C/C++ programmers: Are modes the same as passing by value/reference?
- Or, What's the relation between mode and pass by value/reference
- Briefly:
- Pass by value and pass by reference are parameter passing mechanisms
- Modes specify information flow, not implementation mechanism
- Modes are a contract
- The modes can be implemented using any mechanism
- Mode is chosen by compiler to make code efficient
- Generally: small values are copied in and out
- Generally: large values are passed by reference
Procedure vs Function
Functions are Values; Procedures are Statements
- Procedures calls are statements
-
put_line("Hello"); -- this is a statement
- Function calls represent values
-
y := 3.0 * sqrt(x);
-
sqrt(x)
returns a value used in an expression
- Functions cannot be used as procedures!
procedure foo is
function notSoGood(n) return integer is
begin
put(n);
return -n;
end notSoGood;
begin
notSoGood; -- Compile error!
This is not true in Java and C.
Java and C Can Ignore Expression Results
public class Func {
public static int calcPrintCube(int n){ // Non-void return type
System.out.println(n * n * n); // Side effect: change state of output
return n * n * n;
}
public static void main(String[] args) {
calcPrintCube(10); // function used as statement
int cube = calcPrintCube(20); // return value ignored
}
}
This is legal C:
int calcPrintCube(int n){ // Non-void return type
printf("%d", n * n * n); // Side effect: change state of output
return n * n * n;
}
void main(String args[]) {
calcPrintCube(10); // function used as statement
int cube = calcPrintCube(20); // return value ignored
}
Even this is legal C:
x = 0; // Do you
y - 0; // See the error
z = 0; // Error in this code?
Even this is legal C:
0;
In C, results of expressions can be freely ignored
Procedures or Functions: Which to use
- Question: is it better to use
p(x, y);
or y := p(x);
- If returning a single value:
- if value is non-structured (eg a number): probably better to use a function
- if value is structured (eg an array) probably better to use a procedure:
- it can be modified in place rather being copied on assignment
- individual elements can be modified
- If returning multiple values (eg
p(x, y, z);
to calculate y and z) from x:
- use a procedure
- or put y and z into a structured type (like an array or record - which we cover later)
Recursive Routines
Recursive Routines
- Main routine can call a recursive routine:
procedure call_recur is
function fac(n: Natural) is
begin
if n = 1 then
return 1;
else
return n * fac(n - 1);
end if;
end fac;
begin
put(fac(5));
end call_recur;
Mutually Recursive Routines
- Mutual recursion causes some problems:
- Mutual recursion example: A calls B and B calls A
procedure mutual_recursion is
procedure A is
begin
B;
end A;
procedure B is
begin
A;
end B;
begin
B; -- Infinite recursion!
end mutual_recursion;
This simple example causes infinite recursion
It also won't compile: why??
Mutually Recursive Routines - Problem solved
- Mutual recursion example: A calls B and B calls A
procedure mutual_recursion is
-- Procedure specification
-- Declare that B is a procedure with no parameters
procedure B;
procedure A is
begin
B;
end A;
procedure B is
begin
A;
end B;
begin
B; -- Infinite recursion!
end mutual_recursion;
This simple example causes infinite recursion
Some people like to declare the specifications of all routines at the beginning
Mutually Recursive Routines - Realistic Example
- A more realistic example: a solution to a compilation problem:
- This code evaluates arithmetic expressions, such as 2 * 3 and 2 * (3 + 4)
procedure handle_expressions is
-- Procedure specifications - all of them
procedure handle_parens;
procedure handle_expressions;
procedure handle_expressions is
begin
while has_more_symbols(expr) loop
if is_number(next_symbol) then
handle_number;
elsif is_operator(next_symbol) then
handle_operator;
elsif is_left_paren(next_symbol) then
handle_parens;
end if;
end loop;
end A;
procedure handle_parens is
begin
skip_left_paren;
handle_expressions;
skip_right_paren;
end B;
-- declare other routines here
begin
handle_expressions;
end infinite_recursion;
Global Variables - Why Not?
- If no globals, routines can modify only parameters of mode
out
and in out
- Can ignore other routines
procedure demo is
miles, gals, mpg: Float; !!Don't use globals!!
function calc_mpg(m, g: Float) return Float is
mpg: Float := m / g;
begin
miles := m / g; -- A blunder!
return mpg;
end calc_mpg;
begin -- demo
get(miles);
get(gals);
mpg := calc_mpg(miles, gals);
put(miles);
put(gals);
put(mpg);
end demo;
What if no globals?
Global types and constants are not a problem
- Because they don't lead to the same kind of debugging problems
Modes Help Debugging, if no Global Variables
- Use modes to help find error
procedure p1(a: in Integer);
procedure p2(a: in out Integer);
procedure p3(a: in Integer);
...
x: Integer;
begin -- main
put(x); -- Assume value of x is correct here
p1(x);
p2(x);
p3(x);
put(x); -- What if value of x is not correct here?
...
Error must be in procedure p2
- Only true if no global variables
WARNING: Unitialized Variables
- Uninitialized variables will compile [but not a good idea, of
course]
procedure warning is
procedure p is
someLocal: Natural;
begin
put(someLocal);
end p;
var_for_main: Natural; -- Always put variables for main here
begin -- warning
p;
put(var_for_main);
end warning;
GNAT gives a warning, but compiles
Java would not allow this: definite assignment
- One of the few places where Java is more reliable than Ada
- But there are reasons to not initialize a variable
Double piTimesDiam(double diam){
Double pi; // Forgot the value!
return pi * diam; // Error!
}