Worksheet Argument Passing
Table of Contents
- 1. L-Value or Not?
- 2. Argument Passing
- 3. Returning Two Items
- 4. Scope and Recursive Values
- 5. Solutions
- 5.1. Solution: L-Value or Not?
- 5.2. Solution: CBV in C
- 5.3. Solution: CBV in Java
- 5.4. Solution: Returning Two Items - Java - Immutable Pair Class
- 5.5. Solution: Returning Two Items - Java - Mutable Pair Class
- 5.6. Solution: Returning Two Items - C - Immutable Pair Struct
- 5.7. Solution: Returning Two Items - C - Mutable Pair Struct
- 5.8. Solution: Scope and Recursive Values
1. L-Value or Not?
In each of the following contexts, determine whether the given expression is an l-value or not.
Your code should replace the ellipsis (...
).
Is
x++
an l-value in C? The context is:void f (int x) { ... }
Is
arr[x++]
an l-value in C? The context is:void f (int x) { int arr[] = { 5, 6, 7, 8, 9, 10, 11 }; ... }
Is
f ().x
an l-value in C? The context is:struct S { int x; int y; }; struct S f () { struct S s; s.x = 5; s.y = 6; return s; // returns a copy of the "struct S", i.e., copies the two int members back } void g () { ... }
Is
t.x
an l-value in C? The context is:struct S { int x; int y; }; struct S f () { struct S s; s.x = 5; s.y = 6; return s; // returns a copy of the "struct S", i.e., copies the two int members back } void g () { struct S t = f (); ... }
Is
f ()->x
an l-value in C? Recall that this means(*(f())).x
, i.e., callf
, dereference the pointer, then access thex
member of the struct. The context is:#include <stdlib.h> struct S { int x; int y; }; struct S *f () { struct S *p = (struct S *) malloc (sizeof (struct S)); p->x = 5; // recall that p->x is just shorthand for (*p).x p->y = 6; return p; // returns a copy of the pointer, i.e., copies just a pointer back } void g () { ... }
Is
arr[x++]
an l-value in Java? The context is:class LValue6 { static void f (int x) { int[] arr = { 5, 6, 7, 8, 9, 10, 11 }; ... } }
Is
list.get (x++)
an l-value in Java? The context is:import java.util.ArrayList; class LValue7 { static void f (int x) { ArrayList<Integer> list = new ArrayList<> (); list.add (5); list.add (6); list.add (7); list.add (8); ... } }
Is
list.get (x++).x
an l-value in Java? The context is:import java.util.ArrayList; class C { int x; int y; C (int x, int y) { this.x = x; this.y = y; } } class LValue8 { static void f (int x) { ArrayList<C> list = new ArrayList<> (); list.add (new C (5, 5)); list.add (new C (6, 6)); list.add (new C (7, 7)); list.add (new C (8, 8)); ... } }
2. Argument Passing
2.1. CBV in C - Int
Confirm that C uses call-by-value to pass int
variables by writing a simple C program then running it.
2.2. CBV in C - Pointer
Confirm that C uses call-by-value to pass pointer variables by writing a simple C program then running it.
2.3. CBV in Java - Int
Confirm that Java uses call-by-value to pass int
variables by writing a simple Java program then running it.
2.4. CBV in Java - Reference
Confirm that Java uses call-by-value to pass reference variables (meaning references to instances on the heap) by writing a simple Java program then running it.
2.5. Scala - Reference
When the following Scala program is executed it prints C(10)
.
Can we conclude that Scala uses call-by-reference?
case class C (var x:Int) object Test { def f (o:C) = { o.x = 10 } @main def main () = { val c = C (5) f (c) println (c) } }
2.6. C - Struct
Examine the following C program.
A struct S
contains two int
members (like fields) and an array of int
(length is ARR_SIZE
or 16).
#include <stdio.h> #include <stdlib.h> #define ARR_SIZE 16 struct S { int x; int y; int arr[ARR_SIZE]; }; void print_S (struct S *p) { printf ("S {\n x = %d,\n y = %d,\n arr = [ ", p->x, p->y); for (int i = 0; i < ARR_SIZE; i++) { printf ("%02X, ", p->arr[i]); } printf ("],\n}\n"); } void f (struct S s) { printf ("s in f #1\n"); print_S (&s); s.x = s.x + 1; /* increment s.x (first way to write it) */ s.y += 1; /* increment s.y (second way to write it) */ for (int i = 0; i < ARR_SIZE; i++) { s.arr[i]++; /* increment s.arr[i] (third way to write it) */ } printf ("s in f #2\n"); print_S (&s); } int main (void) { struct S t; t.x = 5; t.y = 6; for (int i = 0; i < ARR_SIZE; i++) { t.arr[i] = i; } //printf ("sizeof(struct S)=%lu\n", sizeof(struct S)); printf ("t in main #1\n"); print_S (&t); f (t); printf ("t in main #2\n"); print_S (&t); }
- How big is a
struct S
? (You can check your answer afterwards by adding a line to printsizeof(struct S)
.) In contrast how big is each field in an instance of this Java class?
class S { int x; int y; int[] arr; }
- The
main
function allocates a variablet
of typestruct S
in its activation on the call-stack. We often say thatt
is stack-allocated. The functionf
is called witht
as an argument. The functionf
prints the contents of its argument, updates its argument, then prints the contents again. Finallymain
prints the contents oft
again. Try to work out will be printed, given the statement "C copies structures when passed as arguments". Confirm your answer by compiling and running the C program. - Rewrite the C program so that
f
receives a pointer to astruct S
, and does its updates via that pointer. Try to work out how that will affect the output. Recompile and run your program to confirm your answer.
3. Returning Two Items
It is often necessary to return two or more items from a function or method.
In Scala, tuple types can be used to return two items.
For example, fibaux
below performs an efficient calculation of the n
and n+1
elements of the Fibonacci sequence.
By returning a pair of two consecutive numbers from the Fibonacci sequence it avoids the unnecessary recomputation in the naive definition of the Fibonacci function.
def fibaux (n:BigInt) : (BigInt, BigInt) = { if (n <= 0) { (0, 1) } else if (n == 1) { (1, 1) } else { val (a, b) = fibaux (n - 1) (b, a + b) } } def fib (n:BigInt) : BigInt = fibaux (n)._1
For example:
scala> (0 to 70).toList.map (n => BigInt (n)).map (fib) res0: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135)
Now consider how to return two or more items in other programming languages.
3.1. Java - Immutable Pair Class
The following Java program defines an immutable Pair
class (with type parameters X
and Y
).
Complete the definition of fibaux
in the following Java program, which uses the immutable Pair
class.
You may need to refer to the Java documentation for use of Java's BigInteger class.
import java.math.BigInteger; class Pair<X,Y> { final X x; final Y y; Pair (X x, Y y) { this.x = x; this.y = y; } } public class Fib1 { static Pair<BigInteger,BigInteger> fibaux (BigInteger n) { ... } static BigInteger fib (BigInteger n) { return fibaux (n).x; } public static void main (String[] args) { for (int i = 0; i < 71; i++) { System.out.println (fib (BigInteger.valueOf (i))); } } }
3.2. Java - Mutable Pair Class
If the final
modifier is removed from the fields of Pair
, the caller to fibaux
can pass in (a reference to) a Pair
instance, and the fields of that instance can be updated with the results.
Complete the definition of fibaux
in the following Java program.
import java.math.BigInteger; class Pair<X,Y> { X x; Y y; Pair (X x, Y y) { this.x = x; this.y = y; } } public class Fib2 { static void fibaux (BigInteger n, Pair<BigInteger,BigInteger> result) { ... } static BigInteger fib (BigInteger n) { Pair<BigInteger, BigInteger> p = new Pair<> (BigInteger.ZERO, BigInteger.ZERO); fibaux (n, p); return p.x; } public static void main (String[] args) { for (int i = 0; i < 71; i++) { System.out.println (fib (BigInteger.valueOf (i))); } } }
3.3. C - Immutable Struct
The following C program defines an immutable pair
struct.
We use long
for simplicity instead of an arbitrary precision data type for integers.
Complete the definition of fibaux
in the following C program, which uses the immutable pair
struct.
#include <stdio.h> #include <stdlib.h> struct pair { const long x; const long y; }; struct pair fibaux (long n) { ... } long fib (long n) { return fibaux (n).x; } int main (void) { for (int i = 0; i < 71; i++) { printf ("%ld\n", fib (i)); } }
3.4. C - Mutable Pair Struct
Explain why we cannot remove the const
modifier from the members of struct pair
and then rewrite fibaux
with the following signature (contrast with the Java mutable Pair
class above).
struct pair { long x; long y; }; void fibaux (long n, struct pair result) { ... }
Explain how to change the signature of fibaux
so that an argument for the result can be passed to fibaux
.
4. Scope and Recursive Values
- Write a Scala value
alternates
of typeLazyList[Boolean]
that alternates betweentrue
andfalse
(starting withtrue
). Try the following expressions in the Scala REPL (after your definition of
alternates
)."the rain in spain".toList "the rain in spain".toList.zip (alternates) "the rain in Spain".toList.zip (alternates).filter ((p:(Char,Boolean)) => p match { case (c:Char,b:Boolean) => b }) /* shorthand for the previous version */ "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b } "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b } val evens = "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => b }.map (p => p._1) val odds = "the rain in Spain".toList.zip (alternates).filter { case (c:Char,b:Boolean) => !b }.map (p => p._1) evens.zip (odds)
5. Solutions
5.1. Solution: L-Value or Not?
- No, temporary value from
++
- Yes, array
- No, temporary value from
f()
- Yes, struct
- Yes, pointer is temporary, but field is not
- Yes, array
- No, temporary Integer
- Yes, all object access via pointers/references in Java
5.2. Solution: CBV in C
#include <stdio.h> void swapInt(int x, int y) { int oldX = x; int oldY = y; x = oldY; y = oldX; } void swapPointer(int *x, int *y) { int* oldX = x; int* oldY = y; x = oldY; y = oldX; } int main() { { int a = 0; int b = 1; printf ("a = %d, b = %d\n", a, b); swapInt (a, b); printf ("a = %d, b = %d\n", a, b); } { int a = 0; int *p = &a; int b = 1; int *q = &b; printf ("*p = %d, *q = %d\n", *p, *q); swapPointer (p, q); printf ("*p = %d, *q = %d\n", *p, *q); } }
5.3. Solution: CBV in Java
class CBVBase { private static void swapBase(int x, int y) { int oldX = x; int oldY = y; x = oldY; y = oldX; } public static void main(String args[]) { int a = 0; int b = 1; System.out.format ("a = %d, b = %d\n", a, b); swapBase (a, b); System.out.format ("a = %d, b = %d\n", a, b); } } class CBVRef { private static void swapRef(Integer x, Integer y) { Integer oldX = x; Integer oldY = y; x = oldY; y = oldX; } public static void main(String args[]) { Integer a = 0; Integer b = 1; System.out.format ("a = %d, b = %d\n", a, b); swapRef (a, b); System.out.format ("a = %d, b = %d\n", a, b); } }
5.4. Solution: Returning Two Items - Java - Immutable Pair Class
import java.math.BigInteger; class Pair<X,Y> { final X x; final Y y; Pair (X x, Y y) { this.x = x; this.y = y; } } public class Fib1 { static Pair<BigInteger,BigInteger> fibaux (BigInteger n) { if (n.compareTo (BigInteger.ZERO) <= 0) { return new Pair<> (BigInteger.ZERO, BigInteger.ONE); } else if (n.equals (BigInteger.ONE)) { return new Pair<> (BigInteger.ONE, BigInteger.ONE); } else { Pair<BigInteger, BigInteger> p = fibaux (n.subtract (BigInteger.ONE)); return new Pair<> (p.y, p.x.add (p.y)); } } static BigInteger fib (BigInteger n) { return fibaux (n).x; } public static void main (String[] args) { for (int i = 0; i < 71; i++) { System.out.println (fib (BigInteger.valueOf (i))); } } }
$ javac Fib1.java $ java Fib1 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920 2504730781961 4052739537881 6557470319842 10610209857723 17167680177565 27777890035288 44945570212853 72723460248141 117669030460994 190392490709135
5.5. Solution: Returning Two Items - Java - Mutable Pair Class
import java.math.BigInteger; class Pair<X,Y> { X x; Y y; Pair (X x, Y y) { this.x = x; this.y = y; } } public class Fib2 { static void fibaux (BigInteger n, Pair<BigInteger,BigInteger> result) { if (n.compareTo (BigInteger.ZERO) <= 0) { result.x = BigInteger.ZERO; result.y = BigInteger.ONE; } else if (n.equals (BigInteger.ONE)) { result.x = BigInteger.ONE; result.y = BigInteger.ONE; } else { fibaux (n.subtract (BigInteger.ONE), result); BigInteger x = result.x; BigInteger y = result.y; result.x = y; result.y = x.add (y); } } static BigInteger fib (BigInteger n) { Pair<BigInteger, BigInteger> p = new Pair<> (BigInteger.ZERO, BigInteger.ZERO); fibaux (n, p); return p.x; } public static void main (String[] args) { for (int i = 0; i < 71; i++) { System.out.println (fib (BigInteger.valueOf (i))); } } }
5.6. Solution: Returning Two Items - C - Immutable Pair Struct
#include <stdio.h> #include <stdlib.h> struct pair { const long x; const long y; }; struct pair fibaux (long n) { if (n <= 0) { return (struct pair) { 0, 1 }; } else if (n == 1) { return (struct pair) { 1, 1 }; } else { struct pair p = fibaux (n - 1); return (struct pair) { p.y, p.x + p.y }; } } long fib (long n) { return fibaux (n).x; } int main (void) { for (int i = 0; i < 71; i++) { printf ("%ld\n", fib (i)); } }
5.7. Solution: Returning Two Items - C - Mutable Pair Struct
It would not work because fibaux
would receive a copy of the struct pair
from the caller, so modifications to result
would not be seen by the caller.
To modify the program, we pass a pointer to a struct pair
instead, and fibaux
fills in its value.
#include <stdio.h> #include <stdlib.h> struct pair { long x; long y; }; void fibaux (long n, struct pair *presult) { if (n <= 0) { presult->x = 0; presult->y = 1; } else if (n == 1) { presult->x = 1; presult->y = 1; } else { fibaux (n - 1, presult); long x = presult->x; long y = presult->y; presult->x = y; presult->y = x + y; } } long fib (long n) { struct pair result; // not initialized here, will be initialized by fibaux fibaux (n, &result); return result.x; } int main (void) { for (int i = 0; i < 71; i++) { printf ("%ld\n", fib (i)); } }
5.8. Solution: Scope and Recursive Values
With safe initialization enabled in Scala 3.0, you need to write it this way:
val alternates = def generator():LazyList[Boolean] = true #:: false #:: generator() generator()
With safe initialization off, you can write simply:
val alternates = LazyList[Boolean] = true #:: false #:: alternates
(It appears that the safe initialization checker does not know that #::
is non-strict.)