The Department of Computer Science & Engineering
cse@buffalo

CSE 305
Programming Languages
Lecture Notes
Stuart C. Shapiro


Subprograms

Definition
A subprogram is a piece of program with a well-defined operational semantics that can be executed (called) from various places in a program. It is useful for two reasons:
  1. It eliminates the need to replicate its code everywhere that that code needs to be executed.
  2. It serves as a process abstraction: the programmer can make use of its operation without being further concerned with how that operation is implemented.

Major Types
There are four types of subprogram:
  1. Procedure: executed for its side-effects
  2. Function: executed for its value
  3. Relational: executed for instances (Prolog)
  4. Method: attached to objects
However, they have more in common than what distinguishes them, so our discussion will mostly be about them all.

General Characteristics
These characteristics [Sebesta, p. 392-393] all have exceptions, but we'll ignore those in the general discussion.

Calling Methods
A function call, consisting of the name of the function and a list of actual parameters, is syntactically an expression, and can be situated wherever an expression of the function's return type can be. C-based languages allow a statement to be nothing but a function call---its value is discarded. Most programming languages require the parentheses after (or around, in the case of Lisp) the function name, even if it takes no arguments.

Fortran has a special statement for calling a procedure, CALL Sub [([<argument> {,<argument}]>)]. Other languages allow a procedure call to be a statement by itself. If a procedure subprogram has no parameters, Fortran allows just the name to appear in the CALL statement. Other languages require the parentheses, even with nothing between them.

A call to a Prolog relational subprogram consists of the name of the relation and a list of argument expressions, which may have variables in them.

<timberlake:Test:1:76> prolog
SICStus 4.0.5 (x86_64-linux-glibc2.3): Thu Feb 12 09:48:30 CET 2009
Licensed to SP4cse.buffalo.edu
| ?- [user].
% compiling user...
|  max(X,Y,Z) :-
      X>Y, Z=X;
      Z=Y.
| printmax(X,Y) :-
     max(X,Y,Z), format("The max of ~d and ~d is ~d.~n", [X,Y,Z]).
|
% compiled user in module user, 0 msec 1264 bytes
yes
| ?-  printmax(3,5).
The max of 3 and 5 is 5.
yes
| ?- printmax(7,2).
The max of 7 and 2 is 7.
yes

Parts of a Subprogram Definition

The header can sometimes appear without the body if the compiler needs the header information to compile calls, but the programmer does not want to provide the body details yet.

Subprogram Bodies
In most current languages, a subprogram body is a block that provides a local binding environment for the subprogram's formal parameters, local variables, and, in some languages, locally scoped subprograms.

In some old programming languages, the body did not provide a separate binding environment, just an entry point and a way to specify return of execution to the caller.

SNOBOL had a dynamically-defined body. There was an entry point, and a local binding environment, but termination was determined by the dynamically next return goto to be executed.

Termination Specification Methods
The issue is how to specify that the subprogram is finished, and execution is to return to the caller, and, for function subprograms, what value is to be returned.

A procedure may return when execution falls through to the bottom. I.e. the body is syntactically one statement; the procedure returns when that statement has been executed.

A return statement may be used. It is an executable statement whose effect is to terminate the procedure and return execution to the calling unit. return is a variety of exit that exits a procedure.

Fortran (77 & earlier) allows the name of a function subprogram to be used as a local variable within its body. The name's value at the time of termination is the value returned by the function.

Pascal also used this technique. However, since Pascal allowed functions to be recursive, using the function name where it would be evaluated for an r-value caused an error: "attempt to call a function with incorrect number of arguments".

Fortran 90 (and later) allows recursive functions, but only if declared so. In that case, the variable whose value is to be used as the result must also be declared:

      Program factTest
      Integer i, fact
      Do i = 1, 4
         Print *, i, fact(i)
      EndDo
      End

      Recursive Function fact(n) Result(result)
      Integer result, n
      If (n .eq. 0) Then
         result = 1
      Else
         result = n * fact(n-1)
      Endif
      End
--------------------------------------------------
<timberlake:Test:1:164> f95 -o factTest factTest.f
<timberlake:Test:1:165> ./factTest
           1           1
           2           2
           3           6
           4          24
Also notice the EndDo instead of a label.

Many current languages allow return <expression>. This causes the function to terminate, and the value of <expression> becomes the value of the function.

Returning Multiple Values
In the discussion of parallel assignment, we saw assignment statements with multiple variables on the LHS. To accommodate this, several languages, including Common Lisp, Perl, Python, and Ruby, allow functions to return multiple values.

In Python and Ruby multiple return values are specified by following return with several expressions separated by commas. For example in Python:

>>> def quoRem(x,y):
... 	return x/y, x%y
... 
>>> q,r = quoRem(10,3)
>>> q
3
>>> r
1
Actually, they package their multiple values in collections, a tuple for Python, an array for Ruby,
>>> quoRem(10,3)
(3, 1)

>>> def printit(x):
... 	print x
... 
>>> printit(quoRem(10,3))
(3, 1)
The LHS of the assignment statement shown above is just an instance of parallel assignment.

Python's functions may return no value:

>>> def nothing():
... 	return
... 
>>> nothing()
>>>
Ruby functions, however, always return something:
<timberlake:Test:1:184> irb
irb(main):001:0> def nothing()
irb(main):002:1>    return
irb(main):003:1> end
=> nil
irb(main):004:0> nothing()
=> nil

Common Lisp functions return multiple values via the values function:

cl-user(147): (defun quoRem (x y)
		(values (floor (/ x y)) (mod x y)))
quoRem
cl-user(148): (quoRem 10 3)
3
1
The values may be stored using one of the functions multiple-value-bind, multiple-value-call, multiple-value-list, multiple-value-prog1, or multiple-value-setq. For example,
cl-user(149): (multiple-value-bind (q r)
		  (quoRem 10 3)
		(format t "The quotient of 10 and 3 is ~d and the remainder is ~d~%" q r))
The quotient of 10 and 3 is 3 and the remainder is 1
nil
Common Lisp does not package the multiple values in a collection, and if used in a context that only expects one value, all but the first are ignored:
cl-user(150): (print (quoRem 10 3))

3 
3
cl-user(151):
Like Python, a Common Lisp function may return no value:
cl-user(151): (defun nothing ()
		(values))
nothing
cl-user(152): (nothing)
cl-user(153):
A good use of multiple values is retrieval from a hashmap. The first value can be the value found, or nil if the key wasn't stored. However, this value of nil then does not distinguish between nothing being found and nil being found. So the second value can be True or False, indicating whether the lookup was successful.

A good use for returning no value is for a function that prints information to the user, in order not to confuse that value printed with the value returned:

cl-user(153): (defun justPrint (x)
		(print x)
		(values))
justPrint
cl-user(154): (justPrint (quoRem 10 3))

3 
cl-user(155):

Subprogram Headers
Name
Most subprograms have names, but in several programming languages, including Common Lisp, Erlang, Haskell, Python, and Ruby, it is possible to create a nameless function. Here's a simple Python example:
>>> (lambda x: x+1)(3)
4

The subprogram name, when it has one, follows the syntactic rules for an identifier, and, in statically scoped languages, has a scope, which is the enclosing block.

Type
Function subprograms have a type, which is the type of the returned value.

Procedures don't return a value, so usually don't have a type.

In C-based languages, a procedure is a function whose type is void.

Formal Parameters
Matching Actual to Formal Parameters
Just about lining up actual to formal parameters, binding formal parameters to values will be discussed later.

Positional Parameters
The usual method for matching actual to formal parameters. The nth actual parameter is matched with the nth formal parameter.

Optional Parameters
In some languages, in some circumstances, it is possible for the number of actual parameters to be less than the number of formal parameters. The extra formal parameters are treated like local variables initialized to the default value, if any.

In SNOBOL there could be more actual parameters than formal parameters. The extra actual parameters were matched to the local variables in order as though they were additional formal parameters.

In Common Lisp optional formal parameters are declared as such:

cl-user(6): (defun reverse (list &optional end)
	      (if (endp list) end 
		(reverse (rest list) (cons (first list) end))))
reverse

cl-user(7): (reverse '(a b c d e))
(e d c b a)
Common Lisp also allows for a sequence of optional arguments to be gathered into a list:
cl-user(5): (defun union* (set &rest sets)
	      (loop with result = set
		  for next in sets
		  do (setf result (union next result))
		  finally (return result)))
union*
cl-user(6): (union* '(a b c) '(c d e) '(e f g) '(h i j))
(j i h g f e d a b c)
Common Lisp optional and rest parameters, if not matched with actual parameters, are initialized, like local variables, to nil. (But see the discussion of default values, below.)

Perl uses the technique of matching a sequence of actual parameters to one array as its only method of parameter matching:

#! /util/bin/perl

sub test {
  print @_[0], @_[1], @_[2], "\n";
}

test("a", "b", "c");

-------------------------------------
<timberlake:Test:1:28> perl paramMatch.perl
abc

Java uses "varargs" to collect extra arguments:

public class RestArg {
    public static void test(String... strings) {
	for (String str : strings) {
	    System.out.println(str);
	}
    }
	public static void main(String[] args) {
	    test("One string", "Second string", "Third string");
	}
}
-----------------------------------------------------------------
<timberlake:Test:1:36> javac RestArg.java

<timberlake:Test:1:37> java RestArg
One string
Second string
Third string

Keyword Parameters
Formal parameter is mentioned with actual parameter in call to indicate they should be matched.
cl-user(14): (defun testKey (&key first second third)
	      (list first second third))
testKey

cl-user(15): (testKey :second 2 :third 'c :first 'I)
(I 2 c)
Also see member
cl-user(16): (member 'c '(a b c d e))
(c d e)

cl-user(17): (member '(3 c) '((1 a) (2 b) (3 c) (4 d) (5 e)))
nil

cl-user(18): (member '(3 c) '((1 a) (2 b) (3 c) (4 d) (5 e))
		     :test #'equal)
((3 c) (4 d) (5 e))

cl-user(19): (member 'March '((January 31) (February 28) (March 31) (April 30))
		     :key #'first)
((March 31) (April 30))

Default Values
Where optional or keyword formal parameters are allowed, the programmer may generally specify a default value to be used if the subprogram is called without a matching actual parameter.

In Common Lisp:

cl-user(6): (defun packem (w x &optional (y 5) (z 7))
	      (list w x y z))
packem

cl-user(7): (packem 1 3)
(1 3 5 7)

cl-user(8): (packem 1 3 11)
(1 3 11 7)

In Python, optional parameters are indicated by supplying default values; any parameters may be passed in keyword fashion; *arg collects extra arguments.

>>> def list(x,y=3):
... 	return x,y
... 

>>> list(6,7)
(6, 7)

>>> list(2)
(2, 3)

>>> list(y=5,x=4)
(4, 5)

>>> def listmore(x,*y):
... 	return x,y
... 

>>> listmore(1,2,3,4,5,6)
(1, (2, 3, 4, 5, 6))
>>> 

In Ruby, optional parameters are also indicated by supplying default values, and *arg collects extra arguments, but keyword parameters are not supported.

<timberlake:Test:1:29> irb
irb(main):001:0> def list(x,y=3)
irb(main):002:1>   return x,y
irb(main):003:1> end
=> nil

irb(main):004:0> list(6,7)
=> [6, 7]

irb(main):005:0> list(2)
=> [2, 3]

irb(main):006:0> def listmore(x,*y)
irb(main):007:1>   return x,y
irb(main):008:1> end
=> nil

irb(main):009:0> listmore(1,2,3,4,5,6)
=> [1, [2, 3, 4, 5, 6]]

Parameter Passing
A motivational example from Fortran 95:
      Program cbr
      Integer i, j
      i = 3
      j = 5
      Print *, "Before call, i = ", i, " j = ", j
      Call Swap(i, j)
      Print *, "After call, i = ", i, " j = ", j
      End

      Subroutine Swap(x, y)
      Integer x, y, temp
      temp = x
      x = y
      y = temp
      End

----------------------------------------------------
<timberlake:Test:1:38> f95 -o cbr.fout cbr.f

<timberlake:Test:1:39> ./cbr.fout
 Before call, i =            3  j =            5
 After call, i =            5  j =            3
By interchanging the values of the formal parameters, subroutine Swap interchanged the values of the actual parameters.

Semantic Parameter Passing Modes
In Mode
The value of the actual parameter is made available to the subprogram via the formal parameter, but the value of the actual parameter cannot be changed by the subprogram. This is what you're used to from Java.

Out Mode
The value of the actual parameter is not made available to the subprogram, but the value of the actual parameter can be changed/set by the subprogram.

Inout mode
The value of the actual parameter is made available to the subprogram via the formal parameter, and the value of the actual parameter can be changed by the subprogram. This is what was demonstrated in the Fortran95 program above.

Implementation Models of Parameter Passing
Pass-by-Name
An inout mode technique where the actual parameter expression textually replaces the formal parameter in the code of the subprogram. Identifier names in the actual parameter expression might accidentally be the same as those of other identifiers in the subprogram, in which case they would be the same variables. One of the few places where something close to pass-by-name is still used is in Common Lisp macros.

A Common Lisp macro is a subprogram that

  • binds its formal parameters to its unevaluated actual argument expressions;
  • and returns a form (a Lisp object that can be evaluated).
  • The form is then evaluated,
  • That value becomes the value of the macro call expression.

Many "functions" in Common Lisp are actually macros defined in Common Lisp itself.

ANSI Common Lisp has no while loop, but the Allegro Common Lisp extension defines while as a macro:

(defmacro while (condition &rest forms)
  `(loop (unless ,condition (return)) ,@forms))
Let's try it:
cl-user(51): (let ((i 1))
	       (while (< i 5) (print i) (incf i)))

1 
2 
3 
4 
nil
The backquote character is new:
Normally, every element of a Lisp expression after the function name is evaluated unless quoted:
cl-user(55): (list 'a 'b (+ 3 2) 'c '(list d e) 'f)
(a b 5 c (list d e) f)

cl-user(56): '(a b (+ 3 2) c (list d e) f)
(a b (+ 3 2) c (list d e) f)
The backquote is like the normal quote:
cl-user(57): `(a b (+ 3 2) c (list d e) f)
(a b (+ 3 2) c (list d e) f)
But anything preceded by a comma is "unquoted"---evaluated
cl-user(60): `(a b ,(+ 3 2) c ,(list 'd 'e) f)
(a b 5 c (d e) f)
If comma precedes a form that will evaluate to a list, putting an "@" after the comma "splices" the list in. (Think Ruby's splat.)
cl-user(61): `(a b ,(+ 3 2) c ,@(list 'd 'e) f)
(a b 5 c d e f)
Look at the definition and use of while again:
(defmacro while (condition &rest forms)
  `(loop (unless ,condition (return)) ,@forms))

cl-user(51): (let ((i 1))
	       (while (< i 5) (print i) (incf i)))

1 
2 
3 
4 
nil
Common Lisp's macroexpand let's us see the form that the macro returns before it is evaluated again:
cl-user(63): (macroexpand '(while (< i 5) (print i) (incf i)))
(block nil
  (tagbody
    #:g319
      (progn (unless (< i 5) (return)) (print i) (incf i))
      (go #:g319)))
t
Notice how the actual argument expressions were bound as values of the formal parameters, and appear in the resulting form.

A problem with pass-by-name occurs when a variable in the actual argument expression is the same as a variable in the code where it is used. Here is an example based on Paul Graham, ANSI Common Lisp, Prentice Hall, Englewood Cliffs, NJ, 1996, p. 165-168.

cl-user(99): (defmacro ntimes (n &body forms)
	       `(loop for x from 1 to ,n
		    do ,@forms))
ntimes

cl-user(100): (ntimes 5 (princ ".") (princ "!"))
.!.!.!.!.!
nil

cl-user(101): (let ((y 10)) (ntimes 5 (incf y)) y)
15
However:
cl-user(102): (let ((x 10)) (ntimes 5 (incf x)) x)
10
We can see the problem if we use macroexpand:
cl-user(103): (macroexpand '(ntimes 5 (incf x)))
(block nil
  (let ((x 1))
    (declare (type real x))
    (tagbody
      excl::next-loop
        (incf x)
        (excl::loop-really-desetq x (1+ x))
        (when (> x '5) (go excl::end-loop))
        (go excl::next-loop)
      excl::end-loop)))
t
Putting that code inside the let, we see the problem more clearly:
(let ((x 10))
  (block nil
  (let ((x 1))
    (declare (type real x))
    (tagbody
      excl::next-loop
        (incf x)
        (excl::loop-really-desetq x (1+ x))
        (when (> x '5) (go excl::end-loop))
        (go excl::next-loop)
      excl::end-loop)))
  x)
The x of the macro shadowed he x of the user's let block.

The solution is to use gensym:

cl-user(104): (defmacro ntimes (n &body forms)
		`(loop for ,(gensym) from 1 to ,n
		     do ,@forms))
ntimes

cl-user(105): (let ((x 10)) (ntimes 5 (incf x)) x)
15

cl-user(106): (macroexpand '(ntimes 5 (incf x)))
(block nil
  (let ((#:g356 1))
    (declare (type real #:g356))
    (tagbody
      excl::next-loop
        (incf x)
        (excl::loop-really-desetq #:g356 (1+ #:g356))
        (when (> #:g356 '5) (go excl::end-loop))
        (go excl::next-loop)
      excl::end-loop)))
t

Pass-by-Value
An in mode technique. The formal parameter is bound to its own memory cell at subroutine call, and the value of the actual parameter is copied into it. The formal parameter then acts like a local variable within the subprogram. The formal parameter is simply deallocated at subprogram termination time.

Pass-by-Result
An out mode technique. The formal parameter acts like an uninitialized local variable in the subprogram, but at subprogram termination time, its value is copied into the l-value of the actual parameter.

Here's an example in C#:

"In C#, arguments can be passed to parameters either by value or by reference... To pass a parameter by reference, use the ref or out keyword." [Passing Parameters (C# Programming Guide)] We will examine the issue of whether out parameters are really pass-by-reference below.
// C# comparison of pass-by-value vs. pass-by-result
// Copied from http://msdn.microsoft.com/en-us/library/0f66670z.aspx
//    and slightly modified.

using System;

class valueVsResult
{
    static void Main(string[] args)
    {
	int x, x2;

        // Passing by value.
        // The value of x2 in Main is not changed.
        x = x2 = 4;
        squareVal(x,x2);
        Console.WriteLine("After pass-by-value x2 = " + x2);
        // Output: 4

        // Passing by result
        // The value of x2 in Main is changed.
        x = x2 = 4; 
        squareOut(x, out x2);
        Console.WriteLine("After pass-by-result x2 = " + x2);
        // Output: 16 
    }

    static void squareVal(int inParameter, int outParameter)
    {
        outParameter = inParameter * inParameter;
    }

    // Passing by reference
    static void squareOut(int inParameter, out int outParameter)
    {
        outParameter = inParameter * inParameter;
    }
}
-------------------------------------------------------------
<timberlake:Test:1:172> mcs valueVsResult.cs

<timberlake:Test:1:173> mono valueVsResult.exe
After pass-by-value x2 = 4
After pass-by-result x2 = 16

Pass-by-Value-Result
An inout mode technique. Acts the same as pass-by-value at subprogram call, and the same as pass-by-result at subprogram termination time. During subprogram execution time, the formal parameter is bound to its own memory cell, distinct from that of the actual parameter.

Pass-by-Reference
An inout mode technique. At subroutine call, the formal parameter is bound to the same memory cell as the actual parameter. Thus, during subprogram execution, the formal parameter is an alias of the actual parameter. This is the technique traditionally used by Fortran. See the example above.

In Fortran IV, pass-by-reference could be used to change the values of literal constants! Later versions of Fortran prevent this.

We can test for pass-by-reference as in the following Fortran program:

      Program pbrTest
      Integer i, j
      i = 3
      j = 5
      Print 10, i,j
 10   Format(" Before call, i = ", i2, " j = ", i2)
      Call testPBR(i, i)
      Print 20, i,j
 20   Format(" After call, i = ", i2, " j = ", i2)
      End

      Subroutine testPBR(x, y)
      Integer x, y
      Print 30, x,y
 30   Format(" testPBR called with x = ", i2, " y = ", i2)
      y = x*x
      Print 40, x,y
 40   Format(" testPBR about to return with x = ", i2, " y = ", i2)
      End
------------------------------------------------------
<timberlake:Test:1:113> f95 -o pbrTest pbrTest.f

<timberlake:Test:1:114> ./pbrTest
 Before call, i =  3 j =  5
 testPBR called with x =  3 y =  3
 testPBR about to return with x =  9 y =  9
 After call, i =  9 j =  5
Notice that since testPBR was called with both actual arguments being the same, the two formal parameters of testPBR were aliases.

C++ also has pass-by-reference:

#include <stdio.h>

void testPBR(int &x, int &y) {
  printf("testPBR called with x = %d y = %d\n", x, y);
  y = x*x;
  printf("testPBR about to return with x = %d y = %d\n", x, y);
}

int main(int argc, char **argv) {
  int i=3, j=5;
  printf("Before call, i = %d j = %d\n", i, j);
  testPBR(i,i);
  printf("After call, i = %d j = %d\n", i, j);
  return 0;
}
------------------------------------------------------------
<timberlake:Test:1:117> g++ -Wall -o pbrTestCPP pbrTest.cpp

<timberlake:Test:1:118> ./pbrTestCPP
Before call, i = 3 j = 5
testPBR called with x = 3 y = 3
testPBR about to return with x = 9 y = 9
After call, i = 9 j = 5

So let's see if out parameters in C# are really pass-by-result or if they are pass-by-reference:

using System;

class pbrTest
{

  static void Main(string[] args)
    {
      Console.WriteLine("C# out pbrTest");
      int i,j;
      i=3;
      j=5;
      Console.WriteLine("Before call, i = " + i + " j = " + j);
      testPBR(out i, out i);
      Console.WriteLine("After call, i = " + i + " j = " + j);
    }

  static void testPBR(out int x, out int y)
    {
      Console.WriteLine("testPBR called with x = " + x + " y = " + y);
      y = x*x;
      Console.WriteLine("testPBR about to return with x = "+ x + " y = " + y);
    }
}
------------------------------------------------------------------
<timberlake:Test:1:136> mcs pbrTest.cs
pbrTest.cs(19,64): error CS0269: Use of unassigned out parameter `x'
pbrTest.cs(20,11): error CS0269: Use of unassigned out parameter `x'
pbrTest.cs(21,73): error CS0269: Use of unassigned out parameter `x'
Compilation failed: 3 error(s), 0 warnings
So x did not use in-mode or inout-mode and was given its own memory address. So, for this C# compiler at least, out parameter passing is pass-by-result. Now let's try ref parameters instead:
using System;

class pbrTest
{

  static void Main(string[] args)
    {
      Console.WriteLine("C# ref pbrTest");
      int i,j;
      i=3;
      j=5;
      Console.WriteLine("Before call, i = " + i + " j = " + j);
      testPBR(ref i, ref i);
      Console.WriteLine("After call, i = " + i + " j = " + j);
    }

  static void testPBR(ref int x, ref int y)
    {
      Console.WriteLine("testPBR called with x = " + x + " y = " + y);
      y = x*x;
      Console.WriteLine("testPBR about to return with x = "+ x + " y = " + y);
    }
}
--------------------------------------------------------------------
<timberlake:Test:1:149> mcs pbrTest.cs

<timberlake:Test:1:150> mono pbrTest.exe
C# ref pbrTest
Before call, i = 3 j = 5
testPBR called with x = 3 y = 3
testPBR about to return with x = 9 y = 9
After call, i = 9 j = 5
So we conclude that ref parameters implement inout-mode semantics, and use the addresses of their actual arguments. Thus, for this C# compiler at least, ref parameter passing is pass-by-reference.

Subtleties
All parameter passing in Java is pass-by-value. However, many variables hold references to objects, so, although the parameter is passed by value, the object is essentially passed by reference:
public class Name {
    public String first, last;

    public Name (String f, String l){
	first = f;
	last = l;
    }

    public String toString() {
	return first + " " + last;
    }

    public static void ReName(Name n, String f, String l) {
	n.first = f;
	n.last = l;
    }

    public static void main (String[] args) {
	
	Name tom = new Name("Tom", "Thumb");
	System.out.println("Tom's name is " + tom);
	ReName(tom, "Betty", "Boop");
	System.out.println("Tom's name is " + tom);
    } // end of main ()
}// Name

----------------------------------------------------
<timberlake:Test:1:174> javac Name.java

<timberlake:Test:1:175> java Name
Tom's name is Tom Thumb
Tom's name is Betty Boop

It's sometimes said that C uses pass-by-value except for arrays, which are passed by reference. But that's not true. C always uses pass-by-value. However, array-valued variables are actually pointer variables that point to the first element of the array. So the situation is like the situation in Java when passing reference variables. C's array is essentially passed by reference because the parameter is really passed by value:

#include <stdio.h>

void swap(int b[]) {
  int temp;
  printf("b = %p\n", b);
  temp = b[0];
  b[0] = b[1];
  b[1] = temp;
}

int main() {
  int a[2] = {3, 5};
  printf("a = %p\n", a);
  printf("Before call, a = [%d, %d]\n", a[0], a[1]);
  swap(a);
  printf("After call, a = [%d, %d]\n", a[0], a[1]);
  return 0;
}
-----------------------------------------------------
<timberlake:Test:1:184> gcc -Wall -o cbv cbv.c

<timberlake:Test:1:185> ./cbv
a = 0x7ffff1b2ace0
Before call, a = [3, 5]
b = 0x7ffff1b2ace0
After call, a = [5, 3]

In C++, an object-valued variable is bound to enough memory on the stack to hold the entire object. When such a variable is passed by value, the actual object cannot be changed by the subprogram:

#include <iostream>
#include <string>
using namespace std;

class Name {
public: string first, last;

  Name (string f, string l){
    first = f;
    last = l;
  }

  string toString() {
    return first + " " + last;
  }
};

void ReName(Name n, string f, string l) {
  n.first = f;
  n.last = l;
}

int main () {
  Name tom("Tom", "Thumb");
  cout << "Tom's name is " << tom.toString() << endl;
  ReName(tom, "Betty", "Boop");
  cout << "Tom's name is " << tom.toString() << endl;
  return 0;
}

------------------------------------------------------------
<wasat:Programs:1:154> g++ Name.cc -o Name.ccout -R /util/gnu/lib

<wasat:Programs:1:155> Name.ccout
Tom's name is Tom Thumb
Tom's name is Tom Thumb

Alternatively, the C++ object can be allocated on the heap, and a reference to it can be assigned to a pointer variable. Passing this variable by value has the same effect as passing a reference variable by value in Java:

#include <iostream>
#include <string>
using namespace std;

class Name {
public: string first, last;

  Name (string f, string l){
    first = f;
    last = l;
  }

  string toString() {
    return first + " " + last;
  }
};

void ReName(Name* n, string f, string l) {
  n->first = f;
  n->last = l;
}

int main () {
  Name* tom = new Name("Tom", "Thumb");
  cout << "Tom's name is " << tom->toString() << endl;
  ReName(tom, "Betty", "Boop");
  cout << "Tom's name is " << tom->toString() << endl;
  return 0;
}

----------------------------------------------------------
<wasat:Programs:1:157> g++ cbp.cc -o cbp.ccout -R /util/gnu/lib

<wasat:Programs:1:158> cbp.ccout
Tom's name is Tom Thumb
Tom's name is Betty Boop

C++ has reference variables and reference types. A C++ reference variable is a constant pointer variable that is implicitly dereferenced. If a formal parameter is a reference variable, it is bound to the address of the actual parameter, giving the same effect as Fortran's call-by-reference:

#include <iostream>
using namespace std;

void swap(int& x, int& y) {
  int temp;
  temp = x;
  x = y;
  y = temp;
    }

int main() {
  int i, j;
  i = 3;
  j = 5;
  cout << "Before call, i = " << i << " j = " << j << endl;
  swap(i, j);
  cout << "After call, i = " << i << " j = " << j << endl;
    }

--------------------------------------------
<wasat:Programs:1:160> g++ cbr.cc -o cbr.ccout -R /util/gnu/lib

<wasat:Programs:1:161> cbr.ccout
Before call, i = 3 j = 5
After call, i = 5 j = 3

All Prolog parameters are passed by inout-mode:

pair(X,Y,Z) :- Z = [X,Y].

showPair(E1,E2,Result) :- pair(E1,E2,Result),
	format("The pair of ~w and ~w is ~w.~n", [E1,E2,Result]).
------------------------------------------------------------------
SICStus 4.0.5 (x86-linux-glibc2.3): Thu Feb 12 09:47:39 CET 2009
Licensed to SP4cse.buffalo.edu
| ?- ['pair.pro'].
% compiling /projects/shapiro/CSE305/Test/pair.pro...
% compiled /projects/shapiro/CSE305/Test/pair.pro in module user, 0 msec 648 bytes
yes

| ?- showPair(a,b,P).
The pair of a and b is [a,b].
P = [a,b] ? 
yes

| ?- showPair(a,Y,[a,c]).
The pair of a and c is [a,c].
Y = c ? 
yes

| ?- showPair(X,d,[a,d]).
The pair of a and d is [a,d].
X = a ? 
yes

| ?- showPair(X,Y,[a,3]).
The pair of a and 3 is [a,3].
X = a,
Y = 3 ? 
yes

| ?- 

Function Parameters
One strength of subprograms is that they can operate on different data during different calls---the subprogram is parameterized. However, usually, if a subprogram calls another subprogram, which subprogram it calls is fixed by the its code. This could also vary from call to call if the subprogram could be passed a subprogram as a parameter.

We already saw one example: the Common Lisp function member can be passed different tests and different keys to use.

Common Lisp and other functional languages include function as one of their data types. Therefore functions can be passed as parameters as easily as other data types. Common Lisp also has functions that apply functions to their arguments:

cl-user(32): (type-of #'+)
compiled-function

cl-user(33): (funcall #'+ 1 2 3 4)
10

cl-user(34): (apply #'+ '(1 2 3 4))
10

cl-user(35): (apply #'+ 1 2 '(3 4))
10

cl-user(43): (type-of #'(lambda (x) (* x x)))
function

cl-user(44): (funcall #'(lambda (x) (* x x)) 3)
9
Here's a function that prints a table of a list of numbers and the results of applying some function to each of those numbers:
cl-user(45): (defun double (x)
	       (+ x x))
double

cl-user(46): (defun sqr (x)
	       (* x x))
sqr

cl-user(47): (defun printTable (f numbers)
	       (loop for x in numbers
		   do (format t "~d  ~d~%" x (funcall f x))))
printTable

cl-user(48): (printTable #'double '(1 2 3 4 5))
1  2
2  4
3  6
4  8
5  10
nil

cl-user(49): (printTable #'sqr '(1 2 3 4 5))
1  1
2  4
3  9
4  16
5  25
nil
Here's the Python version:
def doubleit(x):
    return 2*x

def sqr(x):
    return x*x

def printTable(f, args):
    for i in args:
        print i, apply(f, (i,))

printTable(doubleit, [1,2,3,4,5])
print
printTable(sqr, [1,2,3,4,5])
---------------------------------------
<timberlake:Test:1:204> python printTable.py
1 2
2 4
3 6
4 8
5 10

1 1
2 4
3 9
4 16
5 25
In C, an actual parameter may be the name of a function; the matching formal parameter must be a pointer to a function returning the correct type. "The name of a function is a constant pointer to the function. Its value is the address of the function's machine code in memory." [Peter Prinz and Ulla Kirch-Prinz, (Translated by Tony Crawford), C Pocket Reference, O'Reilly Media, 2002, p. 55.]
#include <stdio.h>

int doubleit(int x) {
  return x + x;
}

int sqr(int x) {
  return x * x;
}

void printTable(int (*f)(), int a[], int length) {
  int i;
  for (i = 0; i < length; i++) {
    printf("%d  %d\n", a[i], (*f)(a[i]));
  }
}

int main() {
  int a[5] = {1, 2, 3, 4, 5};
  printTable(doubleit, a, 5);
  printf("\n");
  printTable(sqr, a, 5);
  return 0;
}

-------------------------------------------------------
<timberlake:Test:1:207> gcc -Wall -o printTable printTable.c

<timberlake:Test:1:208> ./printTable
1  2
2  4
3  6
4  8
5  10

1  1
2  4
3  9
4  16
5  25

Overloaded Subprograms
An overloaded subprogram has one name associated with multiple protocols, each with its own body. "Dispatching" is the process of choosing which body to execute. The decision is based on comparing the call with the various protocols associated with the name.

Polymorphic Subprograms: Dispatching on Type
Polymorphic subprograms dispatch on the type of one or more of the arguments. This is the heart of object-oriented programming: each class in a class hierarchy can define its own version of some method. Which particular method body is executed for a particular call dynamically depends on the type of the principal actual argument (or on the type of several actual arguments).

Dispatching on Number of Parameters
Commonly used in Java for constructors, but can be used in general to give the effect of optional parameters, which Java doesn't have.

As an example, consider the tail-recursive version of factorial in Python:

def factorial(x,result=1):
    if x<=1:
        return result
    else:
        return factorial(x-1,x*result)
---------------------------------------------
< python
Python 2.6.4 (r264:75706, Dec 21 2009, 12:37:31)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import trFact

>>> trFact.factorial(1)
1

>>> trFact.factorial(4)
24
In Java, that is written:
class trFact {
    public static int factorial(int x) {
	return factorial(x,1);
	    }

    public static int factorial(int x,int result) {
	if (x<=1)
	    return result;
	else
	    return factorial(x-1, x*result);
    }

    public static void main(String[] args) {
	System.out.println("1! = " + factorial(1));
	System.out.println("4! = " + factorial(4));
    }
}
--------------------------------------------------------
<timberlake:Test:1:214> javac trFact.java

<timberlake:Test:1:215> java trFact
1! = 1
4! = 24

Dispatching by Pattern

Dispatching by Pattern Matching
In Haskell one can't have one function name associated with different numbers of formal parameters (I think), because a function name must have a secific type. However, one can overload functions using Haskell's pattern matching facility, which we saw in the discussion of pattern-matching assignment.
factorial(0) = 1
factorial(n) = n * factorial(n-1)
-----------------------------------------
<timberlake:Test:1:223> ghci
GHCi, version 6.12.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.

Prelude> :load factorial.hs
[1 of 1] Compiling Main             ( factorial.hs, interpreted )
Ok, modules loaded: Main.

*Main> factorial(0)
1

*Main> factorial(4)
24

*Main> C-d
Leaving GHCi.
This will clearly cause an infinite loop if we call factorial with an argument less than 0. So we can add a guard:
factorial(0) = 1
factorial(n) | n>0
	= n * factorial(n-1)
----------------------------------------------------------
Prelude> :load factorial2.hs
[1 of 1] Compiling Main             ( factorial2.hs, interpreted )
Ok, modules loaded: Main.

*Main> factorial(0)
1

*Main> factorial(4)
24

*Main> factorial(-1)
*** Exception: factorial2.hs:(1,0)-(3,20): Non-exhaustive patterns in function factorial
Note that the error indicated a failure of pattern-matching, not a failure within the body of the function.

If we want to extend the function to negative arguments, we could add another guarded option:

factorial(0) = 1
factorial(n)
	| n<0 = -1
	| n>0 = n * factorial(n-1)
------------------------------------------------
Prelude> :load factorial3.hs
[1 of 1] Compiling Main             ( factorial3.hs, interpreted )
Ok, modules loaded: Main.

*Main> factorial(0)
1

*Main> factorial(4)
24

*Main> factorial(-1)
-1

We could also use this technique to write our own implementation of append:

append([],list)
	= list

append(x:xs, list)
	= x : append(xs,list)

--------------------------------------------------------
*Main> :load append.hs
[1 of 1] Compiling Main             ( append.hs, interpreted )
Ok, modules loaded: Main.

*Main> append([],[1,2,3])
[1,2,3]

*Main> append([1,2,3],[4,5,6])
[1,2,3,4,5,6]

Erlang's dispatching by pattern matching looks a lot like Haskell's. Here's factorial for Erlang:

-module(factorial).
-export([factorial/1]).

factorial(0) -> 1;
factorial(N) when N<0 -> -1;
factorial(N) when N>0 -> N * factorial(N-1).
---------------------------------------------------
<timberlake:Test:1:238> erl
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4  (abort with ^G)

1> c(factorial).
{ok,factorial}

2> factorial:factorial(0).
1

3> factorial:factorial(4).
24

4> factorial:factorial(-1).
-1

5> q().
ok

6> <timberlake:Test:1:239>

Here's an Erlang implementation of append:

-module(append).
-export([append/2]).

append([],List2) ->
	List2;
append([First|Rest],List2) ->
	[First | append(Rest,List2)].
--------------------------------------------------
4> c(append).
{ok,append}

5> append:append([],[2,3,4]).
[2,3,4]

6> append:append([1,2],[2,3,4]).
[1,2,2,3,4]

7> append:append([a,b,c],[d,e,f]).
[a,b,c,d,e,f]

Dispatching by Unification
The definition of factorial in Prolog is similar to that in Haskell and Erlang, except for the fact that Haskell and Erlang are functional, whereas Prolog is relational:
factorial(0,1).
factorial(N,-1) :- N<0.
factorial(N,F) :- N>0, X is N-1, factorial(X,Y), F is N*Y.
-------------------------------------------------------------
| ?- ['factorial.pro'].
% compiling /projects/shapiro/CSE305/Test/factorial.pro...
% compiled /projects/shapiro/CSE305/Test/factorial.pro in module user, 0 msec -88 bytes
yes

| ?- factorial(0,X).
X = 1 ? 
yes

| ?- factorial(4,X).
X = 24 ? 
yes

| ?- factorial(-1,X).
X = -1 ? 
yes
Haskell and Erlang allow literals and variables in the parameter list of the function header, but no unbound variables in the function call.

Prolog allows unbound variables in the function call as well as in the function header, just as it allows unbound variables in the RHS of assignment expressions. Pattern matching when variables are allowed in both expressions being matched is called "unification". Also recall that all of Prolog's parameter passing is by inout mode. Example:

append([], List2, List2).
append([First | Rest], List2, [First | Intermediate]) :-
	append(Rest, List2, Intermediate).

------------------------------------------------------------
<wasat:Programs:1:103> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('append.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/append.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/append.prolog in module user, 0 msec 392 bytes
yes

| ?- append([a, b, c], [d, e, f], X).
X = [a,b,c,d,e,f] ? 
yes

| ?- append(X, [d, e, f], [a, b, c, d, e, f]).
X = [a,b,c] ? 
yes

| ?- append([a, b, c], X, [a, b, c, d, e, f]).
X = [d,e,f] ? 
yes

| ?- append([a, Second], [c,d,e,f], [First, b | Rest]).
Rest = [c,d,e,f],
First = a,
Second = b ? 
yes

| ?- append([X, b, [c], d], [e, X , Y], [a, b, [Y] | Z]).
X = a,
Y = c,
Z = [d,e,a,c] ? 
yes
This figure shows the matching variables of that last example, and how information flows to instantiate them:

Generic Subprograms, User-Defined Overloaded Operators, & Coroutines

A generic subprogram uses basically the same procedure on different types of actual parameters, possibly with some minor changes. For example a sort procedure that can sort a collection of any type of element as long as that type has a "before" method. The details of how before works may differ from type to type, but the sort procedure is the same.

Some languages, e.g. C++, allow the programmer to provide new procedures for the languages operators (+, *, etc.), thus further overloading them.

A coroutine is a subprogram that is an exception to the characteristic that a subprogram is called by a caller, executes until it terminates, and then returns control to its caller. A coroutine may temporarily give control back to its caller, or to another coroutine, and later be resumed from where it left off. A set of coroutines may pass control among themselves, each resuming each time from where it left off.

Read Sebesta.

First Previous Next

Copyright © 2003-2010 by Stuart C. Shapiro. All rights reserved.

Last modified: Thu Apr 15 14:43:03 2010
Stuart C. Shapiro <shapiro@cse.buffalo.edu>