Procedures and Functions
Procedures and Functions: Overview
- Terminology: procedures, functions, methods, routines
- Procedures: declaring and calling
- Functions
- Parameters
- Local variables
- Scope
- Order of declarations
- Parameter modes: In and Out
- Structure charts
- Parameter mode: In Out
- Parameter modes: why
Procedures and Scope
Terminology: Procedures, functions, methods, routines
- Procedures and functions are comparable to Java methods
- Procedure = void method (ie no return value)
System.out.println("HI"); // Java
put_line("HI"); -- Ada
- Declared as
void println(String s) ...
Function = non-void method (ie method with a non-void return type)
twiceStrLen = 2 * s.length(); // Java
twice_str_len := 2 * length(s); -- Ada
- Declared as
class String{ int length() ...
Informally we say routine or subroutine to refer to a procedure or a function
(or method)
Sometimes we informally use procedure when we mean procedure and/or function
Note that procedures are similar to void methods, but
procedures can change the values of their parameters!
Declaring and Calling a Procedure
- Example - a procedure that prints 2 lines:
procedure demo is
procedure printlines is
begin
put("------------------");
put("------------------");
end printlines;
begin
printlines;
-- do something
printlines;
end demo;
Structure of printlines and demo are the same
printlines is nested in demo's declaration section
If there are no parameters, no parentheses needed or
allowed
Procedures and functions can, of course have parameters ...
Procedure with Parameters
- Example - a procedure that prints n lines, with m characters each:
procedure demo is
procedure printlines(numLines, numChars: Natural; c: char) is
begin
for i in 1 .. numLines loop
for j in 1 .. numChars
put(c);
end loop;
new_line;
end loop;
end printlines;
begin
printlines(2, 20, '-');
-- do something
printlines(3, 20, '*');
end demo;
Procedures can, of course, have local variables ...
Procedure with a Local Variable
- Example - a procedure that prints an even number (n or n+1) of lines:
procedure demo is
procedure printlines(n: Natural) is
-- a local variable (that is a constant)
limit: constant Natural := (if n mod 2 = 0 then n else n + 1);
-- an if expression to make limit even
begin
for i in 1 .. limit loop -- print even number of lines
put("------------------");
end loop;
end printlines;
begin
printlines(2);
-- do something
printlines(3);
end demo;
Order of Declarations, Scope, Global Variables
- Important: Ada requires declaration before use
- Thus, declarations usually go in this order:
- Constants
- New types and/or subtypes (which may be defined using the constants)
- Procedures and functions (which may be defined using the above
- Repeat 1, 2, and/or 3, as needed.
- Variables for main routine [Always last, otherwise they are global!]
- Example:
procedure main is
MaxValue: constant Natural := 10;
subtype MyRange is Integer range 1 .. MaxValue;
second: constant MyRange := MyRange'first + 1;
procedure p(m: MyRange) is
someLocal: Natural := 2 * second;
begin
...
end p;
subtype AnotherType is Integer range 1 .. 2 * MaxValue;
procedure q(a: AnotherType) is begin
p(2 * a);
end q;
v1, v2: Natural; -- Always put variables for main here
m1, m2: MyRange;
begin -- main
...
end main;
All procedures and functions must be declared above where they are called
Scope
- The scope of a declaration is
- the set of statements over which the declaration is visible (to
the compiler)
- The scope of a declaration: a declaration is visible ...
- From the beginning of the declaration ...
- to the end statement associated with the declaration section containing the
declaration
- What is scope of variables above
Global Declarations and Variables
- A declaration is global to a procedure if it is
outside the procedure and visible inside the procedure
- A variable is global to a procedure if it is declared
outside a procedure and it is visible within the procedure
- Thus, variables declared above a procedure are known as
global variables
- In this course we don't use global variables
- Except, perhaps, in rare cases where their use is justified
- Why: they make debugging more difficult since
a procedure can modify a global variable that's not in its
parameter list (ie globals increase coupling, a no no)
- Global types and constants are not a problem (since they
don't lead to the same kind of debugging problems)
- We will discuss global variables more later
- Example from above, revised with global variables:
procedure main is
MaxValue: constant Natural := 10;
subtype MyRange is Integer range 1 .. MaxValue;
second: constant MyRange := MyRange'first + 1;
v1, v2: Natural; -- Where are these visible?
procedure p(m: MyRange) is
someLocal: Natural := 2 * second;
begin
...
end p;
subtype AnotherType is Integer range 1 .. 2 * MaxValue;
m1, m2: MyRange; -- Where are these visible?
procedure q(a: AnotherType) is begin
p(2 * a);
end q;
m3, m4: MyRange; -- Where are these visible
begin -- main
...
end main;
WARNING: Unitialized Variables
- Uninitialized variables are allowd
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 allows it
Java would not allow this: definite assignment
Deep Nesting?
- Question: Can we next a procedure within a nested procedure?
procedure nested is
procedure put_table(limit: Natural) is
procedure print_n_lines(n: Natural) is
begin -- print_n_lines
for i in 1 .. n loop
put_line("******************");
end loop;
end print_n_lines;
begin -- put_table
print_n_lines(2);
for i in 1 .. limit loop
put(i); put(i*i); new_line;
end loop;
print_n_lines(3);
end;
begin
put_table(5);
end nested;
Procedures can be nested to any depth
What is the scope of print_n_lines?
Functions
Example Functions
- Functions return values, similar to Java non-void methods
- Functions must be used as a value (ie in a place
that a value is expected, as in an expression)
- Example: Use functions to calculate sum of 1..n and sum of squares of 1..n
procedure demo is
function sumInts(n: Natural) return Natural is
s: Natural := 0;
begin
for i in 1 .. n loop
s := s + n;
end loop;
return s;
end sumInts;
function sumSquares(n: Natural) return Natural is
ssq: Natural := 0;
begin
for i in 1 .. n loop
ssq := ssq + n ** 2;
end loop;
return ssq;
end sumInts;
n, sumUpToN: Natural;
begin
get(n);
sumUpToN := sumInts(n); -- Function call returns a value
put(sumUpToN); -- Procedure call is a statment
put(sumSquares(n)); -- Function call returns a value
end demo;
Return type and return statement are required
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 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);
x: Integer := 11;
begin
put( cube(x) );
No begin, return, end
An Example with Procedures and Functions
- Reminder: Procedures and functions (and everything else) must be
defined before they are used.
- Example: Print a table of sum of squares
procedure proc_funs is
function sum_sqs(n: Natural) return Natural is
sum: Natural := 0;
begin
for i in 1 .. n loop
sum := sum + i * i;
end loop;
return sum;
end sum_sqs;
procedure print_header is
begin
put_line(" i sum_sqs(i)");
put_line(" ********************");
end print_header;
procedure print_table(first, last: Natural) is
begin
print_header;
for i in first .. last loop
put(i);
put(sum_sqs(i));
new_line;
end loop;
end print_table;
starting_value: Natural;
number_of_values: Natural;
begin
get(starting_value);
get(number_of_values);
print_table(starting_value, starting_value + number_of_values - 1);
end proc_funs;
Parameter Modes
Parameters and Information Flow
- 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
- In Java, parameters can only be used to send information in
- Although parameters that are objects can be changed
- Parameter modes allow the direction to be specified in the procedure declaration
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 to called
- Out: from called to caller
- In Out: from caller to called and back
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;
n, sumUpToN, sumSqUpToN: Natural;
begin
get(n);
calcs(n, sumUpToN, sumSqUpToN);
put(sumUpToN);
put(sumSqUpToN);
end demo;
Conceptual information flow:
- actual parameter n 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
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;
n, sumUpToN, sumSqUpToN: Natural;
begin
get(n);
sumUpToN := 0;
sumSqUpToN := 0;
for k in 1 .. n loop
calcs(k, sumUpToN, sumSqUpToN);
end loop;
put(sumUpToN);
put(sumSqUpToN);
end demo;
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 within call
- In Out mode:
- Value expected before call
- May normally modified within call
Parameter Modes Improve Error Detection
- What kinds of parameter mode errors are caught (or cause warnings):
- 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
-- gnatmake -w foo2.adb: warning: "o" is used uninitialized in this function
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(2); -- error: Actual for "o" must be a variable
foo3(c); -- error
foo3(i); -- error
foo4(x); -- Okay
Parameter Mode vs Parameter Passing Mechanism
- What's the relation between mode and pass by value/reference
- Brielfy:
- Pass by value and pass by reference are parameter passing mechanisms
- Modes are a contract
- The modes can be implemented using any mechanism
- Mode is chosen by compiler
- Generally: small values are copied in and out
- Generally: large values are passed by reference
Modes and Swap in Ada and Java
Using Modes: Swap Two Ints in Java
- For comparison, let's write a Java method to swap two integers:
void swap(int a, int b){
int t = a;
a = b;
b = t;
System.out.println("" + a + " " + b);
}
psvm(){
int i = 1;
int j = 2;
swap(i, j);
sop(i); // What gets printed, and why?
Why?
Procedure Swap in Ada
procedure swap(a, b: ??? Integer) is
t: constant Integer := a;
begin
a := b;
b := t;
end swap;
i: Integer := 1;
j: Integer := 2;
begin
swap(i, j);
put(i); -- What gets printed, and why?
Why?
Swap Two Arrays in Java
- Hmmm, can you swap two arrays?
void swap(int[] a, int[] b){
int[] t = a;
a = b;
b = t;
}
psvm(){
int[] i = {1, 1, 1};
int[] j = {2, 2, 2};
swap(i, j);
sop(i[0]); // What gets printed, and why?
}
Swapping Arrays in Ada
- Different from Java, but we'll discuss it later
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. See below.
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)
Keyword, Positional, and Default Parameters
Motivation
- Concern for programmer: easier to read and write
- Reliability: mistakes less likely
Keyword Parameters
Default Parameters
- Parameters can have default values
- Example: for new_line, the default is 1
procedure new_line(Count: Positive := 1) ...
For integer_text_io, the default width is 11 (big enough for 2 billion and a sign)
procedure put(Item: Integer; width: Natural := 11) ...
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
- 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 handle_parens;
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;
Java and C Can Ignore Expression Results
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?
In C, results of expressions can be freely ignored
Side Effects
- calcPrintCube above has a side effect
- A function that modifies state has a side effect
- Ada tries to avoid side effects
- A procedure or function that modifies a global is also said to have a side effect
- We will discuss side effects more later. Here are
some notes on side effects.
What is a Statement?
- A statement modifies the program state:
- Program state:
- values of all variables
- input marker
- output so far
- Open files
- ...
- Examples:
- x := 3;
- get(y);
- put(x);
-
Open (File => f, Mode => In_File, Name => "my_file.txt");
- Contrast: Statements modify state vs functions are values
- In Ada, never the twain shall meet!
Order of Declaration and Global Variables
Order of Declarations (repeated)
- Ada requires declaration before use
- Consequently, declarations usually go in this order:
- Constants
- New types and/or subtypes
- Procedures and functions
- Variables for main routine
- Example:
procedure main is
MaxValue: constant Natural := 10;
subtype MyRange is Integer range 1 .. MaxValue;
second: constant MyRange := MyRange'first + 1;
procedure p(m: MyRange) is
someLocal: Natural;
begin
...
end p;
procedure q is begin
p(3);
end q;
v1, v2: Natural; -- Variables for main must go here
m1, m2: MyRange;
begin -- main
...
end main;
All procedures and functions must be declared above where they are called
Scope (repeated)
- The scope of a declaration is
- the set of statements over which the declaration is visible (to
the ocmpiler)
- The scope of a declaration: a declaration is visible
- From the beginning of the declaration
- to the end statement associated with the declaration section containing the
declaration
- What is scope of
sum
, starting_value
, print_header
Global Variables
- Do NOT declare your main variables like this:
procedure main is
MaxValue: constant Natural := 10;
subtype MyRange is Integer range 1 .. MaxValue;
m1, m2: MyRange; -- Variables for main should NOT go here
procedure p(m: MyRange) is
someLocal: Natural;
begin
...
m1 := 2 * m2;
...
end p;
procedure q is begin
p(3);
end q;
v1, v2: Natural; -- Variables for main must go here
begin -- main
get(v1);
v2 := v1 + 1;
q;
end main;
where are v1, v2, m1, m2 visible?
m1, m2 are global to p
Global variable definition: A variable is global to a block if it is
visible in the entity but not declared in it
IMPORTANT: In this class, all communication with a procedure (or function) must occur through the
parameter list!!!
- In other words: Avoid global variables
- Global constants and types are allowed
- Global variables may be allowed in certain special cases (eg debugging flags)
Global Variables and Java
- Let's contrast the scope rules of Java and Ada
- Fields and other class members?
- Local variables?
- What principles underly scope decisions: this is another discussion
Why Avoid Global Variables
- Globals make debugging difficult
- They increase coupling among procedures
- Example:
procedure demo1 is
procedure before(x: natural) is
g1: integer;
begin
g1 := 2 * x;
put(g1);
end before;
gl: integer := 22;
procedure after(x: natural) is
gi: integer;
begin
gl := 3 * x;
put(gl);
end after;
n: natural := 3;
begin
put(gl); -- 22 - correct
before(n);
after(n);
put(gl); -- 9 - error - where did that error come from?
end demo1;
Side Effects
- Briefly, side effect: either
- an expression that changes the program's state (eg value of a variable), or
- a procedure call that changes something that is not mentioned in the procedure's parameters
- Side effects tend to reduce reliability
- We discuss side effects in more detail later
- Some notes on side effects
Functions and Parameter Modes
Functions and Out Mode
- In Ada83, 95, 05 function parameters must be IN mode
- In Ada12 function parameters can be OUT and IN OUT mode
- But, just because you can do it, does not mean that you should
- Consider this:
pragma ada_2012; -- Use Ada 2012 options
with ada.integer_text_io; use ada.integer_text_io;
procedure test_double is
function double(x: in out integer) return integer is
begin
x := x * x;
return x; -- Strange!
end double;
a, b: integer;
begin
a := 3;
b := double(a);
put(a);
put(b);
end test_double;
So, why allow in out parameters:
- Function parameters were restructed so that functions would not have
side effects
- Side effect: expression that changes the value of a variable (eg y = x++)
- Truth in advertising: functions can already change globals, which is
a side effect
- Another reason: needed for effective expression of pre and post
conditions
Function Parameter Modes, and Parameter Modes
- Ada 2005 and earlier:
- Functions can have only in parameters
- Only procedures can use out mode and in out mode parameters.
- Later we will see that this can reduce side effects and
makes programs more reliable
- Why: increase reliability by decreasing side effects
- Side effect: change in variable value in an expression
- Example:
y := sqrt(x);
-- sqrt changing x would be a side effect
- Ada 2012 allows out params for functions
- Why: Truth in advertising
- Side effects using possible using in mode pointers or globals
- In mode pointers can misleadingly advertise no side effects
- Safety feature: warns of dangerous dependencies (eg if f(x) and x = 0)
- Frequently used to modify a value and return a status
Procedures and Design Goals
Parameter Modes - Context
- Ada Language goals:
- Reliability
- Efficiency (time and space)
- Concern for programmer
- Design reflects goals
- Strong typing of parameters (no implicit coercion)
- Compare with early FORTRAN: No type checking of parameters
- Subtypes - allow even stronger typing
- Clear distinction between statement and function - Reliable
- Parameter modes - contract
- Keyword and default parameters - more reliable and improve life for programmer
- Default parameters - improve life for programmer
Structure Charts
Structure Charts
- Top box represents problem
- Child boxes represents steps needed to solve parent problem
- Steps in child boxes are done left to right
- Top down design
- Stepwise refinement:
- Break problem into steps
- Refine steps
- Repeat until all steps are doable
- Show information flow (ie in, out, in out) on lines between parents and children
- Examples
- P1: Read two numbers and print average
- P2: Read integers until end of file and print number of evens and odds
- P3: Read count, then read count numbers, then print average of these numbers
- P4: Print a sequence of numbers in reverse
- P5: Input two natural n and print sum of 1..n and sum of squares of 1..n
- P6: Input strings until eof, then print heads and tails of even/odd length
strings, respectively
- Can use structure chart to decide how to break program into procedures
Looking Ahead
Looking Ahead: Arrays as Parameters
- If a routine requires an array as a parameter
- then the array must have a named type
- Example:
procedure demo is
-- Declare a new type
type IntArray is array (1 .. 10) of Natural;
procedure p(a: IntArray) is ...
begin
...
end p;
-- Declare a variable
myArray: IntArray;
begin -- demo
p(myArray);
...