390 likes | 402 Views
ICS 313: Programming Language Theory. Module 12: Subprograms. Objectives. Understand design issues in subprograms: parameter passing type checking overloading and parameterized procedures Subprograms Single entry One executing at a time Returns to caller. Basic Subprogram Definitions.
E N D
ICS 313:Programming Language Theory Module 12: Subprograms
Objectives • Understand design issues in subprograms: • parameter passing • type checking • overloading and parameterized procedures • Subprograms • Single entry • One executing at a time • Returns to caller
Basic Subprogram Definitions • Subprogram definition: • Describes the actions of the subprogram • Subprogram call: • Explicit request that the subprogram be executed • Subprogram header: • specifies name, parameters, return type • Parameter profile (signature): • number, order, and types of parameters • Protocol: • Parameter profile and return type
Parameters • Parameters allow the subprogram to manipulate data external to the subprogram • Provide local names for external data • Allow binding of local names to different data during each call • Alternative: non-local variable referencing • Also allows access to external data • Reduces readability of subprogram • Reduces flexibility of programming • Requires changing external state before passing
Positional, Keyword, Defaults • Positional: • Matching of formal to actual parameters done via position in parameter list • Keyword: • labels used to match formal to actual parameters • Default: • Enables only a subset of actual parameters to be provided in the call
Ada, C, Common Lisp, Python function COMPUTE_PAY(INCOME : FLOAT; • TAX_RATE : FLOAT; • EXEMPTIONS : INTEGER := 1) return FLOAT; float compute_pay(float income, float tax_rate, int exemptions = 1) (defun compute-pay (income tax-rate &key (exemptions 1)) def compute_pay(income, tax-rate, exemptions=1):
Scheme and Lisp Examples • (define (collectem . args) args) • (define (displayall . args) • (map display args)) • (defun MAKE-GRAPHIC-SHAPE (instance • &key • (x-dimension 10 x-dimension-supplied-p) • (y-dimension 20 y-dimension-supplied-p) • (x-offset 0 x-offset-supplied-p) • (y-offset 0 y-offset-supplied-p) &rest other-args) …) Defaults evaluated at function call time
Python Examples • def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): • parrot(1000) • parrot(action = 'VOOOOOM', voltage = 1000000) • parrot('a thousand', state = 'pushing up the daisies') • parrot('a million', 'bereft of life', 'jump’) • def cheeseshop(kind, *arguments, **keywords): • cheeseshop('Limburger', "It's very runny, sir.", • "It's really very, VERY runny, sir.", • client='John Cheese', • shopkeeper='Michael Palin', • sketch='Cheese Shop Sketch') • Defaults evaluated at function definition time! (Let’s see an example…)
Procedures vs. Functions • Procedures: • No return value • Computation results provided via: • side effects upon non-local variables. • “result” parameters (in languages that provide this facility) • Functions: • Invocation results in a value (the result) • No side effect necessary (and typically not desired)
Design Issues for Subprograms • Allocation of local variables • Parameter passing methods • Type checking of parameters • Subprograms as parameters and their evaluation environments • Overloading • Separate compilation
Local Referencing Environments • Local variables: • Variables declared inside subprograms • Static local variables: • Allocated once, at beginning of execution • Retains value between invocations • Fast direct addressing • Prevents recursive subprograms • Stack-dynamic local variables: • Rebound each time subprogram invoked • Supports recursive subprograms • Values lost between invocations • Slower indirect addressing
Parameter Passing Methods • Two issues: data direction flow and data access style. • Data direction flow: • In mode: From actuals to formals • Out mode: from formals to actuals • In-out mode: both directions • Data access style: • Value: data value is provided. • Reference: access path to data value provided • See diagram p. 359
Pass by Value (Call by Value) • An implementation of in-mode • Value of Actual used to initialize Formal • Usually done by data transfer • Cost of additional storage and copy • Formal acts as local variable in subprogram • Access usually more efficient once copied
Pass by value example: Swap void swap (int x, int y) { int z; z = x; x = y; y = z;} main () { int a = 1; int b = 2; swap (a, b); return a;} • What is the return value of this program? 1. Main’s a is not changed.
Pass by Result (Return by Value) • An implementation of out-mode • Formal acts as local variable in subprogram • Efficient access • Value of Formal copied to Actual upon exit • Also requires extra space and time to copy • Combined with Pass by Value this is “Pass by Value-Result”, implementing inout-mode
Pass by Result Examples • Problems with actual parameter collision Header: sub(a, b) Call: sub(p, p) • Value of p depends on order in which a, b copied Sub(list[i], i) • Values depend on time at which address of actuals determined
Pass (Call) by Reference • Like pass-by-value-result, this implements inout-mode • Formal receives access path (e.g., pointer) to memory cell of Actual • Passing is efficient • Access slower due to indirect addressing • Formal used as local variable in subprogram, but assignments change the Actual • Flexible but dangerous unintended changes
Pass by Reference: Swap void swap (int *px, int *py) { int z; z = *px; *px = *py; *py = z;}main () { int a = 1; int b = 2; swap (&a, &b); return a;} • What does this program return? • 2. It works as intended.
Pass by value-result pseudocode foo (x,y) { x = y;}pseudo main () { int i = 2; int j = 3; foo (i, j); return i;} • What does this program return? (Ans: 3) • What would this program return under pass-by-value or pass-by-reference? (Ans: 2, 3)
Pass (Call) by Name • As if actual textual substitution of arguments for parameters • Late binding: Arguments evaluated each time they are assigned or referenced (rather than at call time) • Can be flexible • Potential for name conflicts • Slow and difficult to implement • Not common in modern programming languages
Pass (Call) by Name: Swap void swap (int x, int y) { int z; z = x; x = y; y = z;} swap(a, b) => z = a; a = b; b = z; // works swap(i, A[i]) => z = i; i = A[i]; A[i] = z; // does not assign the A[i] expected
Parameter passing conventions • C++ • Default is call-by-value • & operator provides call-by-reference • Java • Pass by value (including references) • Objects effectively passed by reference • Common Lisp, Scheme, Prolog, Python • Pass by value
Parameter Type Checking • Good for reliability but less flexible • Weakly typed: LISP, Python • Doesn’t check: FORTRAN 77 • Do: Pascal, FORTRAN 90, Java, Ada … • Optional: ANSI C double sin(x) double x; { … } double sin (double x) { … } Optional: C++ printf(const char*, …); printf(“the sum is %d\n”, sum); // sum not checked
Passing Arrays • Pass by reference preferred • How are mapping functions computed? • Java • Arrays are (single dimensional) objects • Size passed with array • C, C++ • Need to know size of arrays at compile time • Separate compilation complicates this • Let’s look at the mapping function for two dimensional C arrays …
Passing Arrays in C: Conclusion • Thus the compiler needs to know M, the number of columns void fun (int matrix[][10]) { … } • Can’t write generic functions! • Workaround: Pass pointer to array and pass array dimensions as arguments. void fun(float *mat, int rows, int cols); • Programmer does the math: *(mat + (i * cols) + j) = x • Avoid this uglyness with macros …
Passing Subprograms as Parameters • Examples: • Accumulator (e.g., Scheme assignment) (accumulate + 0 f a next b)) (accumulate * 1 f a next b)) • Numerical Integration (sampling a function) • Mapping (e.g., in Python:) >>> def consquare(x): ... return (x, x*x) >>> map(consquare, [1,2,3,4,5]) [(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
Passing Subprograms: Type Checks • In typed languages, need to tell the compiler the type of the subprogram. Subprogram type is its protocol • Example: Numeric Integration in Pascal procedure integrate( function f (x:real) :real; lowerbd, upperbd : real; var result : real); var funval, currarg : real; begin . . . funval := f(currarg); . . . end;
Passing Subprograms: Type Checks • Example: C • Only pointers can be passed • “Pointer contains protocol information” • In Caller: int strcmp(), numcmp(); swap() … sort(ptr, nlines, numcmp, swap) • In called: int (*comp)(), (*exch)(); … if ((*comp) (v[j], v(j+1]) <= 0) …
Passing Subprograms: Execution Environment • Shallow binding: • Environment that subprogram is executed within • Consistent with dynamically scoped variables • Deep binding: • Environment that subprogram is declared within • Consistent with lexically scoped variables • Ad-hoc binding: • Environment that subprogram is passed from • Not used
Overloaded Subprograms • Multiple subprograms with the same name • Distinguished by different protocols • Each subprogram has its own implementation • “Ad-hoc” in the sense that there is no abstraction for the generalized program (the programmer writes each version)
Overloaded Subprograms public static void foo(Integer i) { do some Integer stuff … } public static void foo(String s) { do some String stuff … } public static void foo(String s, int len) { do some other String stuff … } // More than one way of overloading: public int compareTo(Object rhs) { … }
Another Use of Overloading public static void writeBackwards(String s) { int len = s.length(); • if (len == 0) { • System.out.println(); • } • else { • System.out.print(s.charAt(len - 1)); • writeBackwards(s.substring(0, len - 1)); • } }
Another Use Continued public static void writeBackwards2(String s) { writeBackwards2(s, s.length()); } public static void writeBackwards2(String s, int len) { if (len == 0) { System.out.println(); } else { System.out.print(s.charAt(len - 1)); writeBackwards2(s, len - 1); } }
Generic Subprograms • Subprogram templates that can be “instantiated” for different types of parameters • Each subprogram shares a common “shell” of code • Differences are restricted to parameters: “parametric polymorphism” • Example: C++ Templates template <class Type> Type max(Type first, Type second) { return first > second ? first : second; } • Compiler makes copy of code for each type • Dynamic binding of types avoids this (e.g., Java methods)
Compilation • Large system development must avoid total recompilation whenever possible • Separate compilation (e.g., Java): • Compilation units can be compiled at different times • Requires access to other units during compilation for type checking • Independent compilation (e.g., FORTRAN 77): • Units can be compiled without access to others • Errors at compilation unit interfaces are caught at link-time or run-time
Coroutines • Symmetric control (“quasi-concurrency”) • Mutually invoke each other • Multiple entry points • Invocation resumes computation at an entry point • State is remembered • Let’s look at some examples in Scheme • Implemented with Call-with-current-continuation (prepare for mind-warp) • This captures the state of the runtime stack … a radical idea!