LMU | CMSI 386
PROGRAMMING LANGUAGES
Practice Questions
1. Syntax and Semantics
1. Consider a new syntactic form $A\textrm{^} B$ which denotes $A | ABA | ABABA | ...$. Such a form makes it convenient to write rules involving separators, such as
IdList = identifier ^ ","

This form can also be used to model a construct representing one or more $A$’s, rather than using $A^+$ or $AA^{*}$ or $A^{*}A$. Show how to do this.

We would need a symbol for the empty string; let’s say we have $\varepsilon$ for that. Then the construct $A^+$ can also be written as $A\textrm{^}\varepsilon$.
2. Give the macrosyntax of a little language with integer-valued variables, assignment statements, while statements, read and write statements, and the usual arithmetic, relational operators, such that type checking between integers and booleans is done in the syntax. Hint: you will need separate syntactic categories for arithmetic and logical expressions.

Program   =  Block
Block     =  (Stmt ";")+
Stmt      =  Id "=" Exp
| "write" Exp ("," Exp)*
| "while" BoolExp "loop" Block "end"
Exp       =  Exp1 ([+-] Exp1)*
Exp1      =  Exp2 ([*/] Exp2)*
Exp2      =  "-"? Exp3
Exp3      =  INTLIT | ID | "(" Exp ")"
BoolExp   =  BoolExp1 ("or" BoolExp1)*
BoolExp1  =  BoolExp2 ("and" BoolExp2)*
BoolExp2  =  Exp (("<" | "<=" | "==" | "!=" | ">=" | ">") Exp)?
| BoolExp3
BoolExp3  =  "not"? BoolExp4
BoolExp4  =  "true" | "false" | "(" BoolExp ")"

3. Suppose we wanted to modify the language Iki by (1) making assignments be expressions, as they are in C, rather than statements, (2) adding function declarations and function calls, and (3) allowing four types: integers, booleans, doubles and strings.
1. Give the macrosyntax (for the whole modified language, not just the parts that have changed). Note that the addition of types means you will need literals for each type as well as additional operators to make use of them. Type checking does not need to be done syntactically.
2. Write an abstract syntax for the new language.
3. Write a natural semantics for the new language.
4. Write a denotational semantics for the new language.
4. In Ada, comments begin with -- and go to the end of the line. Perhaps because of this, the designers decided not to make the unary negation operator have the highest precedence. Instead, expressions are defined as follows:
Exp  = Exp1 ("and" Exp1)* | Exp1 ("or" Exp1)*
Exp1 = Exp2 (relop Exp2)?
Exp2 = "-"? Exp3 (addop Exp3)*
Exp3 = Exp4 (mulop Exp4)*
Exp4 = Exp5 ("**"  Exp5)? | "not" Exp5 | "abs" Exp5

Explain why this choice was (probably) made. Also, give an abstract syntax tree for the expression -8 * 5 and explain how this is similar to and how it is different from the alternative of dropping the negation from Exp2 and adding - Exp5 to Exp4.
5. Explain the need for the elsif keyword in Perl, Ruby, and Ada. (The reason is the same for all three languages.) Then propose changes to these languages’ syntax that preserve the syntactic prevention on the dangling-else, but avoids the funny word. Hint: you only have to change the syntax clause for the if-statement; you do not have to add any new clauses.
6. Give a grammars for the languages
1. $\{w \in \{a,b,c\}^* \;|\; w \textrm{ has at most one occurrence of any symbol}\}$
Sa | b | c | aa | ab | ac | ba | bb | bc | ca | cb | cc | aaa | aab | aac | aba | abb | abc | aca | acb | acc | baa | bab | bac | bba | bbb | bbc | bca | bcb | bcc | caa | cab | cac | cba | cbb | cbc | cca | ccb | ccc
2. $\{a^m b^n c^{m+n} \;|\; m, n \geq 1 \}$
7. Give grammars for the languages
1. $\{a^n b^n c^n \;|\; n \geq 0 \}$
2. $\{a^i b^j c^k \;|\; i = j \vee j = k \}$
3. $\{ww \;|\; w \in \{a, b\}^* \}$
8. Give regular expressions for the following languages without using any kind of “negation” operator. Use only concatenation, alternation, Kleene star and Kleene plus.
1. A number from the set $\{34, 36, 37, 59, 57, 365\}$.
3[467]|5[97]|365
2. The set of all strings over $\{a, b, c\}$ that do not contain two consecutive a’s anywhere in the string.
3. The set of all strings over $\{a, b\}$ that do not have two consecutive $a$’s nor two consecutive $b$’s anywhere in the string.
4. The set of all strings over $\{a, b, c\}$ that do not contain three consecutive $a$’s anywhere in the string.
5. The set of all strings over $\{a, b\}$ that contain neither the substring $aba$ nor $babb$ anywhere in the string.
9. C does not allow structs to be tested for equality (by value). Ada and some other languages do allow this. How exactly does this complicate the compiler or the runtime system?
10. Give the abstract syntax tree for the following C++ expression
(a = 3) >= m >= ! & 4 * ~ 6 || y %= 7 ^ 6 & p

11. Give an abstract syntax tree for the following Ada code fragment:
if x > 2 or not F(F(X)) then
Put (-3 * Q);
elsif not Here or There then
loop
while Close loop Try_Harder; end loop;
X := X ** 3 mod 2*X;
end loop;
Q.all(4).G(6) := Person.List(2);
else
raise Hell;
end if;


12. Give the abstract syntax for the following C fragment:
int f(int x, int y) {
return y,x?a.p[5]+=7&!y<x<y+++x||+x|*&y-~--y*x^y:0;
}

13. Give the abstract syntax for the following C fragment:
printf("%08X %#16.7e\n",x---a.p[9]^x|*&p&1,*((float*)&x));

14. Give the abstract syntax tree for the following Perl fragment (die and split are both list operators):
die "\n">>1,~\x^split @a,3|x**$;=>%p&&&p;  15. Give the abstract syntax tree for the following Perl fragment: sub f { print$x[0] unless q/pig =~ m!^r$!/ =~ m!^\((4)+!x; push @_, sort reverse keys %$shift, g A 1, 2
}

16. Here is a cool little functional language:
Program  =  (Decl ";")* Exp
Decl     =  val ID "=" Exp
|  fun ID "(" Params? ")" "=" Exp
Exp      =  NUMLIT | ID | uop Exp | Exp bop Exp
|  Exp "?" Exp ":" Exp |  ID "(" Args? ")" | "(" Exp ")"
Params   =  ID ("," ID)*
Args     =  Exp ("," Exp)*
uop      =  "-" | "abs" | "not"
bop      =  "+" | "-" | "*" | "/" | "mod" | "and" | "or" | "==" | "<"

1. Why is this called a functional language?
2. Is the grammar ambiguous? Why or why not?
3. Write a Greatest Common Divisor function in this language.
fun gcd(x, y) = y == 0 ? x : gcd(y, x mod y)
4. Give three examples of syntax errors and three examples of static semantic errors in this language. Make sure to write down all your assumptions; I did not give you any semantics so you will have to make up something reasonable.
17. Give macrosyntax rules for expressions in a language in which:
• The lowest precedence operators are the binary unless, if, while, and until operators, which are non-associative.
• The next lowest precedence operators are the logical binary operators, which are left associative amongst themselves, but do not associate with each other, meaning that one while can write A and B and C, one may not write A and B or C.
• The next lowest precedence operators are the relational operators (<, <=, ==, !=, >=, and >), which are non-associative.
• Next in precedence come the left associative shift operators (<< and >>)
• Next come the left multiplicative operators (*, /, and %)
• Next come the left associative additive operators (+ and -)
• Next come the right associative exponentiation operator (**)
• Next come non-associative unary operators. The unary operators - and not are prefix, while ! (for factorial) is postfix. Because these are non-associative, one cannot write - - 2 or -5! or 3!!!.
• The most primitive expressions are identifiers, numeric literals, string literals, ranges (of the form [e1 .. e2] for expressions e1 and e2), function calls, and of course, parenthesized expressions.

Assume the existence of tokens ID, NUMLIT, STRINGLIT, and CALL.

EXP  ::= EXP1 (('if' | 'while' | 'unless' | 'until') EXP1)?
EXP1 ::= EXP2 ('and' EXP2)* | EXP2 ('or' EXP2)*
EXP2 ::= EXP3 (RELOP EXP3)?
EXP3 ::= EXP4 (SHIFTOP EXP4)*
EXP5 ::= EXP6 (MULOP EXP6)*
EXP6 ::= EXP7 ('**' EXP6)?
EXP7 ::= ('-' | 'not')? EXP8
EXP8 ::= EXP9 '!'?
EXP9 ::= ID | NUMLIT | STRINGLIT | CALL | '(' EXP ')' | '[' EXP '..' EXP ']'

18. Here is the description of a language. Programs are made up of a non-empty sequence of function declarations, terminated by semicolons, followed by a single expression. Each function declaration starts with the keyword fun followed by the function's name (an identifier), then a parenthesized list of zero or more parameters (also identifiers) separated by commas, then an equals sign, then the body which is a single expression. Expressions can be numeric literals, string literals, identifiers, function calls, or can be made up of other expressions with the usual binary arithmetic operators (plus, minus, times, divide) and a unary prefix negation and a unary postfix factorial ("!"). There is also an infix binary operator called then which indicates that both of its expressions be evaluated in order from left to right, with the value of the right expression being the value of the entire then-expression. There's a conditional expression that looks just like the one in Java, C, and Perl (with the question mark and colon). Factorial has the highest precedence, followed by negation, the multiplicative operators, the additive operators, conditional and finally the then operator. Parentheses are used, as in most other languages, to group subexpressions. Numeric literals are non-empty sequences of decimal digits with an optional fractional part and an optional exponent part, as in Ada but without the fancy "#" notation. String literals are as in C. Identifiers are those non-empty sequences of letters, decimal digits, underscores, at-signs, and dollar signs, beginning with a letter or dollar sign, that are not also reserved words. Function calls are as in C, with the arguments in a comma-separated list of expressions bracketed by parentheses. There are no comments in this language, and whitespace can be used liberally between tokens.
1. Write an example program in this language that shows off everything described above.
2. Give a syntax for this language.
3. Write a program in this language that consists of the declaration of the GCD function followed by a call to this function with the arguments 99 and 66.
4. Show the abstract syntax tree for your GCD program.
19. In Lisp and its dialects, most of the arithmetic operators are defined to take two or more arguments, rather than strictly two. Thus (* 2 3 4 5) evaluates to 120, and (– 16 9 4) evaluates to 3. Show that parentheses are necessary to disambiguate arithmetic expressions in Lisp (in other words, give an example of an expression whose meaning is unclear when parentheses are removed). Why then, does the author Michael L. Scott say in his book that “Issues of precedence and associativity do not arise with prefix or postfix notation?” Reword this claim to make explicit the hidden assumption.

Scott is assuming all operators have a fixed arity. What he meant to say was: “Issues of precedence and associativity do not arise with prefix or postfix notation assuming all operators have fixed arity.”
2. Classification of Errors
1. Identify the following errors as syntactic, static semantic, or dynamic semantic (runtime).
1. Redeclaration of an identifier in Ada.
2. Unbalanced parentheses.
3. Applying an operator to an element of the wrong type in Ada.
4. Array index out of bounds in Ada.
5. Integer division by zero in Ada.
6. Semicolon after a class body in Java.
7. Wrong number of arguments supplied to a call in Java.
8. Assignment of a variable of type $T$ to a variable of type subtype of $T$ where the first variable is out of the range of the second in Ada.
9. An unwanted infinite loop.
10. Dereference of a null pointer in Ada.
11. Application of the . to an identifier which is not a field of the record (or struct) in Ada.
12. Use of an uninitialized variable in Ada.
13. for I in A'Range loop Put("*"); X := X+1; end loop; (Ada).
2. Identify each of the following C90 code fragments as either (a) a lexical error, (b) a syntax error (c) a static semantic error, (d) a dynamic semantic error, or (e) not an error. Again, these are code fragments, not complete programs.
1. {int x; int x;}
2. {int x; {int x;}}
3. {int x; struct x {};}
4. void f() {return 5;}
5. x +. 2;
6. x = (((int)x)-*0);
7. x---!-+~*&x
8. x = (((int)x)-5));
9. x>&x
10. if (x == 2) {return 1;}; else {return 2;};
11. int a[5]; a[5]=10;
12. float y; double x = y / 0.0;
13. Hawaii = 2;
14. int y = 0; *y = 7;
15. main(5, 0, 0, 0, 0, 0); /* a call to the usual main() function */
16. printf("%d\n", x, y, z);
17. class C {}
18. struct {int x;} p; p.y;
19. int f() {int* p = p;}
20. int main() {main();}
21. int f() {int p; return p;}
3. Identify each of the following as either (a) a lexical error, (b) a syntax error (c) a static semantic error, (d) a dynamic semantic error, or (e) not an error. All items are complete programs in the language Standard ML — that is, they have no dependence on external bindings.
1. let val x = 2 val x = 3 in x end;
2. let val x = 5; val x = "dog" in x end;
3. let val x = print 5 in x end;
4. let val x = print "5" in x end;
5. let val y = ref 2; val x = while (!y < 1) do print "*" in x end;
6. let val y = ref 2; val x = while (!y != 1) do print "*" in x end;
7. let val x = 3 in () end;
8. let val p = *^$q in () end; 3. Pointers and References 1. If possible, write a program in Modula 3 that makes a variable point to itself. That is, for some designator X, make it so that X^ = X. If this is not possible, state why it is not possible. 2. If possible, write a program in Ada that makes a variable point to itself. That is, for some designator X, make it so that X.all = X. If this is not possible, state why it is not possible. 3. If possible, give Ada type and object declarations to make a variable point to itself, that is, make it so that P = P.all. If it is not possible to do so, state why no such variable can be defined. 4. If possible, give C++ type and object declarations to make a variable point to itself, that is, make it so that p == *p. If it is not possible to do so, state why no such variable can be defined. 5. In C++ you can say (x += 7) *= z but you can't say this in C. Explain the reason why, using precise, technical terminology. See if this same phenomenon holds for conditional expressions, too. What other languages behave like C++ in this respect? 6. It is a well-known irritation that Ada does not allow you to write array aggregates for zero- or one-element arrays, e.g., A := (3) gives a static semantic error when A is a one-element array of Integer. Why is this so? Propose a (trivial) syntactic extension to Ada that would remove this irritation. 4. Names, Scopes, and Bindings 1. Give three examples from C which a variable is live but not in scope. Make sure each example is of a different quality than the others; for example, don't just hide three global variables in a single function and claim you have three examples. 2. In Ada, the declarations X: Integer := X + 1; Foo: Foo; Bar: Real := Bar(Foo);  (where global declarations of X, Foo and Bar are visible) are all illegal, since a declaration of an identifier hides global declarations of the same name immediately at the point it appears in the text, but the identifier may not be used until its declaration is complete. Give an alternate interpretation under which these declarations would be legal and explain the advantages and disadvantages of it from both the programmer's and the compiler writer's perspectives. 3. This fragment of Java code illustrates something about scope. Or does it? Relate it to other similar problems we've seen regarding scope. (Don't forget to come across as being articulate and intelligent in your discussion.) public void fail() { class Failure extends RuntimeException {} throw new Failure(); }  4. What would this program output under static scope rules? Under dynamic scope rules? declare x = 2; sub f() {print x;} sub g() {declare x = 5; f(); print x;} g(); print x;  5. What does this script print under (a) static scope rules and (b) dynamic scope rules? var x = 1 function f() {return x;} function g() {var x = 2; return f();} print g() + x  (a) 2, (b) 3 6. Show the output of the following, assuming dynamic scope and (a) deep binding, and (b) shallow binding. function g(h) { var x = 2; h() } function main() { var x = 5 function f() { print x + 3 } g(f) } main()  (a) 8, (b) 5 7. Show the output of the following, assuming dynamic scope and (a) deep binding, and (b) shallow binding. function f(a) { var x = a - 1 function g() { print x - 17 } h(g) } function h(p) { var x = 13 p() } f(18)  8. Why has no (major) language combined static scope and shallow binding? 9. I couldn't find in the defintion of C whether the language employs shallow or deep binding. Why? 10. Explain what would need to be done to make deep binding work with dynamic scoping, assuming that A-lists were used to implement the scope rules. (Hint: think of turning the A-lists into "A-trees".) When you call the passed function, save the existing pointer to the top of the A-list and replace it with a pointer to just before the point that the function was defined. Then enter the bindings for the new function as a "branch" in the A-list (which is now a tree). When the function finally returns, chop off that branch and restore the pointer. 5. Types 1. Is JavaScript 100% weakly typed? About what percentage is it? Why, exactly, is it not? No, JavaScript is not 100% weakly typed; The values null and undefined do not get coerced to objects, and non-functions do not get coerced to functions. I would say it's roughly 90% weakly typed. It’s not 100% weakly typed because its designer thought that coercing things to functions and even objects would be going too far, since there is no consensus on the obvious coercion to make. 2. In the Java programming language, if the class Dog were a subclass of class Animal, then objects of class Dog[] would be compatible with the type Animal[]. Write a fragment of Java code that shows that this means that Java is not completely statically typed. Include in your answer a well-written explanation that shows you truly understand the difference between static and dynamic typing. If both Dog and Rat are subclasses of Animal, this code  Animal[] pets = new Dog[4]; pets[0] = new Rat();  compiles fine but when executed throws an ArrayStoreException, that's right, a run-time typecheck error. This means Java is NOT 100% statically typed because this typecheck occurs at run time. A language can only be called 100% statically typed if all type conflicts are detected at compile time. 3. Here's a variation of M-J. Dominus' Spectacular Example. local fun split [] = ([],[]) | split [h] = ([h], []) | split (x::y::t) = let val (s1,s2) = split t in (x::s1,y::s2) end fun merge c ([], x) = x | merge c (x, []) = x | merge c (h1::t1, h2::t2) = if c(h1,h2)<0 then h1::merge c(t1,h2::t2) else h2::merge c(h1::t1,t2); in fun sort c [] = [] | sort c x = let val (p, q) = split x in merge c(sort c p, sort c q) end; end;  1. During type inference, give the types assigned to • the parameter c within sort • the function split • the function merge • the y in the third clause of split? 2. Give the type of sort and explain why it is not what you would expect. 3. How do you rewrite the function to make it actually do a mergesort? 4. Here's some code in some language that looks exactly like C++. It is defining two mutually recursive types, A and B. struct A {B* x; int y;}; struct B {A* x; int y;};  Suppose the rules for this language stated that this language used structural equivalence for types. How would you feel if you were a compiler and had to typecheck an expression in which an A was used as a B? What problem might you run into? 5. Consider the following C declaration, compiled on a 32-bit little endian machine: struct { int n; char c; } A[10][10];  If the address of A[0][0] is 1000 (decimal), what is the address of A[3][7]? 6. Explain the meaning of the following C declarations: double *a[n]; double (*b)[n]; double (*c[n])(); double (*d())[n];  7. Consider the following declaration in C: double (*foo(double (*)(double, double[]), double)) (double, ...);  Describe rigorously, in English, the type of foo. 6. Control Flow 1. The following pseudocode shows a midtest loop exit: while (true) line := readLine(); if isAllBlanks(line) then exit end; consumeLine(line); end;  Show how you might accomplish the same task using a while or repeat loop, if midtest loops were not available. (Hint: one alternative duplicates part of the code; another introduces a Boolean flag variable.) How do these alternatives compare to the midtest version? 2. Assume we wanted to write a function called If in Java or C or JavaScript, such that the call If(c, e1, e2) would return e1 if c evaluated to true, and e2 if it evaluated to false. Show why, in these languages, such a function is absolutely not the same as the conditional expression c?e1:e2. You can show a code fragment that would return different results based on whether the function were called versus the conditional expression were evaluated. In Java, C, and JavaScript, all function arguments are evaluated before they are called. The evaluation of (p==null ? null : p.value) is null-safe, whereas the call If(p==null, null, p.value) is not: it throws a NullPointerException if p is null. A more blatant example can be seen in the difference between (true ? 1 : formatMyHardDrive()) and If(true, 1, formatMyHardDrive()). 7. Subroutines 1. The language Ada does not require the parameters to a subprogram call to be evaluated in any particular order. Is it possible that different evaluation orders can lead to different arguments being passed? If so, give an example to illustrate this point, and if not, prove that no such event could occur. 2. What exactly must be the case for a subprogram to not need a static link in its stack frame? Think up as many cases as possible. 3. In C++ it is not permitted to have two functions that differ only in return type overload each other. In Ada it is allowed. What is the reason for this situation? Even though Ada does allow this flexibility in overloading, the compiler needs some sophistication. What exactly is involved? Be very precise in your explanation and illustrate it with code fragments. 4. Many programming languages require that in order to have mututally recursive functions, the programmer first define one header (name, return types, parameters and parameter types), then the entire second function, then the entire first function. For example, in C++: int f(int x, char y); void g(int x) {if (x < 0) f(2, 'c');} int f(int x, char y) {g(randomInteger());}  In C++, when f is finally declared, the names of the formal parameters don't have to be repeated exactly as they appeared in the incomplete specification. But in Ada they do. Explain why the Ada rule makes life much easier for the compiler writer. 5. Write a function that takes in a function f and a list [a0, a1, ..., an-1] and returns the list [a0, f(a1), f(f(a2)), f(f(f(a3))), ...]. For example, if you pass as arguments the function that doubles its inputs, and the list [4, 3, 1, 2, 2], then the return value would be [4, 6, 4, 16, 32]. Hint: Do the f(f(f...)) as a separate function. Also you do not have to make your function tail recursive. 6. Write a tail-recursive JavaScript function that produces the sum of squares in an array. This is just to give you practice with tail-recursion; I know there are better ways to compute the sum-of-squares. function ss(a) { function s(i, acc) { return i == a.length ? acc : s(i+1, a[i]*a[i]+acc) } return s(0, 0); }  7. Write a tail-recursive Ruby method that produces the sum of squares in an array. This is just to give you practice with tail-recursion; I know there are better ways to compute the sum-of-squares. def ss(a) s = Proc.new{|i,acc| i==a.length ? acc : s[i+1, a[i]*a[i]+acc]} s[0, 0] end  8. In as many languages as you can (but include C, Java, JavaScript, and Python for sure), write a pair of functions, f and g, such that every time you call f, you get back 5 less than the result of the previous call to f or g, and every time you call g, you get back double the absolute value of the result of the last call to f or g. The initial value is 0. It is possible to do this in one line of Perl. 9. Write the following function in Standard ML, where your implementation must be tail recursive. Given: a list [a0, a1, ..., an-1], Return: a0*a1 + a2*a3 + a4*a5 + .... For example, if given [3, 5, 0, 28, 4, 7] we return 15 + 0 + 28 = 43. If there are an odd number of elements in the list, assume there is an extra 1 for padding. Here is, by the way, a non-tail-recursive formulation: fun sum_of_prods [] = 0 | sum_of_prods (x::nil) = x | sum_of_prods (x::y::t) = x * y + sum_of_prods t;  10. Write a JavaScript function, without using eval, that accepts an array of integers a, and a function f, and returns an array of functions, each of which, when called, invokes f on the corresponding element of a. For example, if your function was called g, then calling g([6,3,1,8,7,9], dog) would return an array of functions p such that, for example, calling p[3]() would invoke dog(8). function g(a, f) { var b = []; for (var i = 0; i < a.length; i++) { b[i] = function(i){return function(){f(a[i])};}(i); } return b; }  11. Write some JavaScript that adds a new method to arrays so that if I call this method on an array with two parameters f and g, I get back a new function which, when called with one argument k, returns the composition of f and g applied to the kth element of the original array. Hint: If we defined the functions square and addSix the obvious way, and we called this new method weird, then: [4, 6, 7, 3, 5, 2, 4].weird(addSix, square)  would return the function z such that z(2) == 55  because the element at index 2 within the array is 7 and$7^2 + 6 = 55$. 12. In Ada, C, and C++ arrays and records can be allocated on the stack, not just on the heap. When making assigments of aggregates to variables, compilers usually generate code to deposit the values in temporary storage. Why is this necessary in general? After all, in Weekdays := Day_Set(False, True, True, True, True, True, False);  we could construct the aggregate directly in the variable Weekdays. Give an example of an assignment statement that illustrates the necessity of constructing an aggregate in temporary storage (before copying to the target variable). 13. In Java, you generally implement callbacks via registration of listeners that implement a known interface, rather than using method pointers. Create a Swing component called AngleReader which displays a picture of a circle and notifies all its listeners of the angle, in degrees, that the mouse cursor makes with the horizontal axis of the circle as the mouse moves over it. 14. Complete the following definition of a dot product function in ML: val dot = let fun transpose ... = in .... end;  The transpose function should work like this transpose ([x1,...,xn],[y1,..,yn]) = [(x1,y1),...,(xn,yn)]  raising Domain if the arrays have different lengths. The body of the definition of dot (between the in and end) should contain only instances of the functions transpose, o, foldr, map, op*, op+, and the value 0. 15. Explain what is printed under (a) call by value, (b) call by value-result, (c) call by reference, (d) call by name. x = 1; y = [2, 3, 4]; function f(a, b) {b++; a = x + 1;} f(y[x], x); print x, y[0], y[1], y[2];  1. Under call by value, the arguments do not change; so the script prints 1 2 3 4. 2. Under call by value/result, the increment of x does not take place until after the subroutine returns. It prints 2 2 2 4. 3. Under call by reference, the increment of b changes x immediately, so the new value of x, namely 2, is used to update a, which is still y[1], and that becomes 2 + 1 = 3, so it prints 2 2 3 4. 4. Under call by name, b++ changes x immediately so x becomes 2. Then, since a refers to the expression {y[x]}, it will need to compute y[2] which is 3. The script prints 2 2 3 3. 16. Explain what is printed under (a) call by value, (b) call by value-result, (c) call by reference, (d) call by name. x = 1; y = 2; function f(a, b) {a = 3; print b, x;} f(x, x + y); print x;  1. Value: 3 1 1 2. Value-Result: 3 1 3 3. Reference: 3 3 3 4. Name: 5 3 3 17. Using your favorite language and compiler, write a program that determines the order in which subroutine arguments are evaluated. 18. Consider the following (erroneous) program in C: void foo() { int i; printf("%d ", i++); } int main() { int j; for (j = 1; j <= 10; j++) foo(); }  Local variable i in subroutine foo is never initialized. On many systems, however, the program will display repeatable behavior, printing 0 1 2 3 4 5 6 7 8 9. Suggest an explanation. Also explain why the behavior on other systems might be different, or nondeterministic. 19. Give an example which shows that default parameters are unnecessary in C++ because you can always get the desired effect with overloading. 20. What does the following program output? with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure P is A: Integer := 4; type T is access Integer; B: T := new Integer'(4); C: T := new Integer'(4); procedure Q (X: in out Integer; Y: T; Z: in out T) is begin X := 5; Y.all := 5; Z.all := 5; end Q; begin Q (A, B, C); Put (A); Put (B.all); Put (C.all); end P;  21. In some implementations of an old language called Fortran IV, the following code would print a 3. Can you suggest an explanation? (Hint: Fortran passes by reference.) More recent versions of the Fortran language don't have this problem. How can it be that two versions of the same language can give different results even though parameters are officially passed "the same way." Note that knowledge of Fortran is not required for this problem.  call foo(2) print* 2 stop end subroutine foo(x) x = x + 1 return end  8. Modules, Classes, and Abstract Data Types 1. Make a Perl module with a function called nextOdd. The first time you call this subroutine you get the value 1. The next time, you get a 3, then 5, then 7, and so on. Show a snippet of code that uses this subroutine from outside the module. Is it possible to make this module hack-proof? In other words, once you compile this module, can you be sure that malicious code can't do something to disrupt the sequence of values resulting from successive calls to this function? 2. Consider the implementation of a Container class framework with the following abstract base class Container: template <class Item> class Container { public: unsigned numberOfItems() {return currentSize;} bool isEmpty() {return currentSize == 0;}; virtual void flush() = 0; ~Container() {flush();} private: unsigned currentSize; };  Here the idea is that each particular (derived) container class shall implement its own flush() operation (which makes sense because different containers are flushed in different ways: there may be arrays, linked lists, rings or hashtables used in the representation), and when a container is destroyed its flush() operation will be automatically invoked. However, the idea is flawed and the code as written causes a terrible thing to happen. What happens? 9. Object-Orientation 1. What philosophers call a "class" mathematicians call a "set" (i.e., a collection of unordered, unique, values). So, a philosopher says that every member of a subclass is also a member of the superclass. But a C++ programmer says that every member of a superclass is also a member of its subclass! What is going on here? 2. Write a three-page paper on the nature of identity in object oriented philosophy. Include some code fragments to illustrate your main points. 3. It is certainly possible to make a Person class, then subclasses of Person for different jobs, like Manager, Employee, Student, Monitor, Advisor, Teacher, Officer and so on. But this is a bad idea, even though the IS-A test passes. Why is this a bad idea and how should this society of classes be built? 4. One of the most important ways in which object oriented programming helps us to manage complexity is through the ability to group related classes into a hierarchy of subclasses and superclasses. Dynamic binding (run-time polymorphism) allows us to operate on collections of objects from different classes in a hierarchy safely. Furthermore, systems which are programmed using dynamic binding are more easily extendible. 1. What construct is used in non-object oriented programming languages to simulate class hierarchies, and why do we say that it is unsafe? 2. Why are inheritance-like structures in non-OOPLs harder to extend? 5. Why is it said that implementation inheritance is at odds with encapsulation? 6. Inheritance is not always appropriate. Discuss the reasons why a design with a superclass Person and subclasses for different jobs (e.g., programmer, manager, ticket agent, flight attendant, supervisor, student, etc.) is a lousy design. Give an alternative. 7. In designing a class hierarchy in C++, when should you make an operation virtual and when should you make an operation non-virtual? Give examples. 8. Write a Perl "class" for machine parts that have an identification number (a positive integer divisible by 5), a weight (a positive floating-point number) and a name (which must consist entirely and exclusively of alphabetic characters). Provide a "constructor" that takes in a string consisting of the id, weight, and name, respectively in which • the string may have leading and trailing spaces • the three fields are separated by a vertical bar • the weight is not expressed in scientific notation: it can have an integral value, but if it does have a decimal point, then it is followed by a non-empty fractional part. There is no "E" part, ever. It's simple. The constructor will check for a valid argument by matching against a regex, and if all is cool, will split the string to assign to its fields. 9. Find some old "procedural" code you have written and rewrite in an object-oriented fashion. I don't mean that you have to use inheritance or polymorphism; all I am really looking for is that you wrap some functions up in a sensible class. 10. A common pattern that comes up a lot is the need to assign unique identifiers to objects of a given class, for example: class Item { private int id; private static int numberOfItemsCreated = 0; public Item() {id = numberOfItemsCreated++;} // pretend that there are more members here... };  As you can see, every item that gets created will get a unique id. Because this pattern occurs frequently, it might be nice to generalize this and make make something reusable out of it so we don't have to write this code inside every class that needs ids. Perhaps we need an interface or abstract class. Tell me why these two suggestions won't work with a detailed, technical answer. Then tell me something that will work. (Note: there is nothing wrong with the access modifiers above; the problems with my two suggestions have to do with the nature of interfaces and abstract classes.) 11. Given a utility class, can you always rewrite it as a singleton? Given a singleton, can you always rewrite it as a utility class? If so, when would you choose one over the other? 12. Write C++, Java, Smalltalk, and Ada declarations corresponding to the following class diagram: 13. Explain how, in C++, you can get access to, and indeed modify, a protected component of an object that someone else declared. As a concrete example, let's say someone has declared  class C {protected: int x; ...}; C c;  then your job is to assign a new value to c.x. Assume there are no public operations of C that modify x that you know of. Also, do not use any preprocessor tricks (like #define protected public). 14. What makes more sense, to inherit a list from a stack or a stack from a list? 15. Why did the designers of the C++ standard library containers emphatically reject an inheritance hierarchy of containers? 16. In C++ you can write class C {int x;}; C c; C* p = &c; cout << p;  and there is no compile-time nor link time error, despite the fact that operator<<(C*) is not a member of ostream (since that class was declared before you declared C), nor for that matter did anyone declare the global function  ostream& operator<<(ostream&, C*);  So why does it all work? Explain exactly what gets printed and why. 17. What happens to the implementation of a class if we "redefine" a field in a subclass? For example, suppose we have: class Foo { public int a; public String b; } ... class Bar extends Foo { public float c; public int b; }  Does the representation of a Bar object contain one b field or two? If two, are both accessible, or only one? Under what circumstances? Answer for C++, Java, Python, and Scala. 18. If Foo is an abstract class in a C++ program, why is it acceptable to declare variables of type Foo*, but not of type Foo? Does this problem even make sense to ask in Java or Python? 10. Concurrency 1. Implement a priority queue data type in Ada, using a server task to provide synchronization. 2. Implement a priority queue data type in Ada, where each priority queue object is a protected object. 3. In the example Ada package implementing the Set data type with a guardian task, there is a serious problem with the package design: errors in insertion are not handled well! What happens if we run out of memory? (Answer in terms of the system interfaces.) Show how to add robust error handling to the package and comment on the amount of parallelism permitted with your solution. 4. Discuss the difficulties of implementing a secure Post Office object in Ada that meets the following requirements. The post office is to maintain a collection of P.O. boxes, each belonging to some task. Any task can put a letter into another task's box, but only the owner of a particular box can open it and read the letters. 5. A relay is an agent task created by one task to relay a message to another. For example, if a calling task wishes to send a message to another but does not wish to wait for a rendezvous, the caller can create a relay task to send the message. (Note that relays are only appropriate to use when there are no out parameters in the called entry.) Sketch in detail a body for an Ada task T that uses a relay R to call entry E of task U, passing message X. 6. In Ada, if two tasks are executing a Put procedure at the same time, their outputs may be interleaved. (This could produce amusing and even distasteful results, e.g. writing "sole" in parallel with "ash") Show how to set things up so only one task is writing at a time. 7. Why do Java programmers not have to worry about the situation in the previous problem (interleaving of text output written to a stream)? 8. One of the nice features of Quicksort is that it allows a great deal of parallelism in an implementation. After partitioning, the slices on either side of the pivot can be sorted in parallel. It is very easy to set things up to do this in languages such as occam, but tedious in Ada and Java. Code up a parallel version of Quicksort in Ada or Java and explain why it is messy. 9. In Ada, what happens when you try to call an entry in a task that has terminated? Comment on the following code fragment as a possible approach to calling entry E of task T only if T has not terminated. if not T'Terminated then T.E; end if;  10. In Java, what happens if you invoke a method on a thread that has completed? 11. Here's an open-ended question many students hate, especially on exams. Two JavaScript programmers are arguing over the best way to implement a little timer widget. The first programmer prefers: var countDown = function () { var i = 10; var update = function () { document.getElementById("t").innerHTML = i; if (i-- > 0) setTimeout(update, 1000); } update(); }  The second argues that this is better: var countDown = function () { var update = function (i) { document.getElementById("t").innerHTML = i; if (i-- > 0) setTimeout(function () {update(i)}, 1000); } update(10); }  Your role is to figure out which programmer is right, if any. Make a fairly extensive list of the pros and cons of each approach. Your list will be graded on completeness, neatness, correct usage of terminology (don't forget to mention "anonymous function" and "closure"), and how articulately you express yourself. Bad grammar will impact your grade negatively. You may want to consider readability and (especially) performance. 11. Ruby Programming 1. Write a Ruby method that takes in a string s and returns the string which is equivalent to s but with all ASCII vowels removed. For example, in irb: >> strip_vowels("Hello, world") => "Hll, wrld"  2. Write a Ruby method that randomly permutes a string. By random we mean that each time you call the method for a given argument all possible permutations are equally likely (note that "random" is not the same as "arbitrary.") For example, in irb: >> scramble("Hello, world") => "w,dlroH elol"  3. Write a Ruby generator method that yields powers of two starting at 1 and going up to some limit. For example, in irb: >> powers_of_two(70) {|x| puts x} 1 2 4 8 16 32 64 => nil  4. Write a Ruby generator method that yields powers of an arbitrary base starting at exponent 0 and going up to some limit. For example, in irb: >> powers(3, 400) {|x| puts x} 1 3 9 27 81 243 => nil  5. Write a method that interleaves two arrays. If the arrays do not have the same length, the elements of the longer array should end up at the end of the result array. For example, in irb: >> interleave(["a", "b"], [1, 2, true, nil]) => ["a", 1, "b", 2, true, nil]  6. Write a method that doubles up each item in an array. For example, in irb: >> [5,4,[3],9].stutter => [5,5,4,4,[3],[3],9,9]  7. Write a Ruby script (in the file prefixes.rb) that writes successive prefixes of its first input argument, one per line, starting with the first prefix, which is zero characters long. For example: $ ruby prefixes.rb matsumoto

m
ma
mat
mats
matsu
matsum
matsumo
matsumot
matsumoto

8. Write a ruby script (in the file lines.rb) that reports the number of non-blank, non-commented lines in the file named by the first argument. Blank lines are those that have either no characters or consist entirely of whitespace; commented lines are those that begin with the "#" character. For example, if the file states.txt has 50 non-blank, non commented lines:
$ruby lines.rb states.txt 50  9. For each of the following code fragments, explain what happens when evaluating them. Make sure you convey in your explanation a thorough understanding of Ruby objects, classes, singleton classes, etc. Note that I have used the pre-1.9.2 notation for singleton class access, to see how well you can think on your feet! dog="spike"; class <<dog; def bark; "arf"; end; end; dog.bark  Evaluates to "arf", since we essentially gave this one dog its own bark method, by attaching it to its own singleton class. class <<"sparky"; def bark; "woof"; end; end; "spraky".bark  class <<"sparky"; def bark; "woof"; end; end; "sparky".bark  dog="sparky"; class <<dog; def bark; "woof"; end; end; dog.bark  Evaluates to "woof", because we attached the bark method to the object referenced by the variable dog, through its metaclass. class <<"sparky"; def bark; "woof"; end; end; "sparky".bark  Raises a NoMethodError because the object in whose metaclass we added the bark method WAS NOT the same string object we tried to call bark on. class <<:sparky; def bark; "woof"; end; end; :sparky.bark  Raises a TypeError because Ruby does not allow metaclasses on symbol objects. Maybe this is because Ruby treats symbols in such a super-special way an never puts them in the object pool. It probably holds them as plain old small integers. class <<2; def bark; "woof"; end; end; 2.bark  raises a TypeError for the same reason as for symbols. class Counter attr_reader :val; @val = 0; def bump(); @val += 1; end end Counter.new.bump; Counter.new.val  10. One of the freshman tried to write a Ruby dot-product method. It came out like this: def dot(a, b) a.zip(b).map{|x,y| x*y}.inject{|x,y| x+y} end  1. Her first unit test failed. What was her test, and why did it fail? The first unit was assert_equal(dot([], []), 0) and it failed because her method returned nil, not 0. 2. She fixed that problem with this def dot(a, b) a.zip(b).inject(0){|x,y| x+y[0]*y[1]} end  Eventually she wrote a unit test with the first array longer than the second and got a TypeError. She fixed that by raising an ArgumentError if the arguments to dot had different lengths. Show her fixed up method. def dot(a, b) raise ArgumentError if a.length != b.length a.zip(b).inject(0) {|x,y| x+y[0]*y[1]} end  3. Then she got fancy and put her method into the array class class Array def *(a) ..............her code here.............. end end  And the unit tests worked: [3,4,2] * [1,5,0] == 23 for example. But this fancy * definition had a very nasty side effect, which, if she did this in real production code, would have probably broken something big time. What went down with this definition? There is already a * operator in the array class; any new definition replaces the old one. Old code using Array * int and Array * string will break badly, because ints and strings can’t be converted to arrays. 12. Scala Programming 1. Write a Scala function that accepts a number of U.S. cents and returns a tuple containing, respectively, the smallest number of U.S. quarters, dimes, nickels, and pennies that equal the given amount. scala> change(96) res2: (Int, Int, Int, Int) = (3,2,0,1) scala> change(8) res3: (Int, Int, Int, Int) = (0,0,1,3)  2. Write a Scala function that takes in a string s and returns the string which is equivalent to s but with all ASCII vowels removed. scala> stripVowels("Hello, world") res4: java.lang.String = Hll, wrld  def stripVowels(s: String) = { """[aeiouAEIOU]""".r.replaceAllIn(s, "") }  3. Write a Scala function that randomly permutes a string. By random we mean that each time you call the function for a given argument all possible permutations are equally likely (note that "random" is not the same as "arbitrary"). scala> scramble("Hello, world") res5: java.lang.String = w,dlroH elol  4. Write a Scala function that yields successive powers of two starting at 1 and going up to some limit. Consume the values with a callback. scala> powersOfTwo(70, (p:Int) => {println("I got a " + p)}) I got a 1 I got a 2 I got a 4 I got a 8 I got a 16 I got a 32 I got a 64  def powersOfTwo(limit: Int, callback: Int => Unit) = { powers(2, limit, callback) }  5. Write a Scala function that yields powers of an arbitrary base starting at exponent 0 and going up to some limit. Consume the values with a callback. scala> powers(3, 400, (p:Int) => {println("I got a " + p)}) I got a 1 I got a 3 I got a 9 I got a 27 I got a 81 I got a 243  6. Write a Scala function that interleaves two lists. If the lists do not have the same length, the elements of the longer list should end up at the end of the result list. scala> interleave(List("a", "b"), List(1, 2, true, null)) res8: List[Any] = List(a, 1, b, 2, true, null)  7. Write a Scala function that doubles up each item in a list. scala> stutter(List(5,4,List(3),9)) res9: List[Any] = List(5, 5, 4, 4, List(3), List(3), 9, 9)  8. Write a Scala script (in the file prefixes.scala) that writes successive prefixes of its first input argument, one per line, starting with the first prefix, which is zero characters long. Assuming the string "matsumoto" were entered, the output would be: $ scala prefixes.scala matsumoto

m
ma
mat
mats
matsu
matsum
matsumo
matsumot
matsumoto

9. Write a Scala script (in the file lines.scala) that reports the number of lines in the file named by the first argument. For example, if the file states.txt has 50 lines:
$scala lines.scala states.txt 50  10. Consider the problem of determining whether two trees have the same fringe: the same set of leaves in the same order, regardless of internal structure. An obvious way to solve this problem is to write a function flatten that takes a tree as argument and returns an ordered list of its leaves. Then we can say def sameFringe(t1: Tree[Any], t2: Tree[Any]) = flatten(t1) == flatten(t2)  1. Write a straightforward version of flatten in Scala. 2. Given your straightforward code in part (a), how efficient is sameFringe when the trees differ in their first few leaves? 3. Rewrite flatten so that sameFringe does not suffer from the problem you identified in part (b). 13. Perl Programming 1. What can't you do with a Perl package named m, s, or y? 2. Write Perl subroutines that takes in a file handle and returns a reference to a hash that maps an integer x to the number of lines in that file that contains x characters (not including the newline), up until the point in the file that contains two blank lines. For example, if the file contains Blah dog dog 123 Four Zero 1234567890* More stuff! 1234  Then you should return the hash reference {4=>3,11=>2,0=>1} because, before the part in the file with two blank lines, there are three lines with four characters, two lines with 11 characters, and 1 line with zero characters. The lines after the two blank line sequence are not considered, nor are the lines in the two blank line sequence. If there is no place in the file with two blank lines, then all lines will be counted. 14. Java Programming 1. What's wrong with this, if anything, and why? class Pair implements Cloneable { private Object first, second; public Pair(Object x, Object y) {first = x; second = y;} public getFirst() {return first;} public getSecond() {return second;} }  15. Clojure Programming 1. Write a Clojure function that accepts a number of U.S. cents and returns a tuple containing, respectively, the smallest number of U.S. quarters, dimes, nickels, and pennies that equal the given amount. user=> (change 96) [3,2,0,1] user=> (change 8) [0,0,1,3]  2. Write a Clojure function that takes in a string s and returns the string which is equivalent to s but with all ASCII vowels removed. user=> (stripVowels "Hello, world") "Hll, wrld""  3. Write a Clojure function that randomly permutes a string. By random we mean that each time you call the function for a given argument all possible permutations are equally likely (note that "random" is not the same as "arbitrary"). user=> (scramble "Hello, world") "w,dlroH elol"  4. Write a Clojure function that yields successive powers of two starting at 1 and going up to some limit. Consume the values with a callback. user=> (powersOfTwo 70 (fn [p] (println "I got a " p)) I got a 1 I got a 2 I got a 4 I got a 8 I got a 16 I got a 32 I got a 64  5. Write a Clojure function that yields powers of an arbitrary base starting at exponent 0 and going up to some limit. Consume the values with a callback. user=> (powers 3 400 (fn [p] (println "I got a " p)) I got a 1 I got a 3 I got a 9 I got a 27 I got a 81 I got a 243  6. Write a Clojure function that interleaves two lists. If the lists do not have the same length, the elements of the longer list should end up at the end of the result list. user=> (interleave '("a" "b") '(1 2 true null)) ("a" 1 "b" 2 true null)  7. Write a Clojure function that doubles up each item in a sequence. user=> (stutter '(5 4 (3) 9)) (5 5 4 4 (3) (3) 9 9)  8. Write a Clojure script (in the file prefixes.clj) that writes successive prefixes of its first input argument, one per line, starting with the first prefix, which is zero characters long. Assuming the string "matsumoto" were entered, the output would be: $ java -cp clojure.jar clojure.main prefixes.clj matsumoto

m
ma
mat
mats
matsu
matsum
matsumo
matsumot
matsumoto

9. Write a Clojure script (in the file lines.clj) that reports the number of non-blank, non-commented lines in the file named by the first argument. Blank lines are those that have either no characters or consist entirely of whitespace; commented lines are those that begin with the "#" character. For example, if the file states.txt has 50 non-blank, non commented lines:
$java -cp clojure.jar clojure.main lines.clj states.txt 50  16. Python Programming 1. Write a Python function that accepts a number of U.S. cents and returns a tuple containing, respectively, the smallest number of U.S. quarters, dimes, nickels, and pennies that equal the given amount. Use the Python divmod built-in function. >>> change(96) (3, 2, 0, 1) >>> change(8) (0, 0, 1, 3)  2. Write a Python function that takes in a string s and returns the string which is equivalent to s but with all ASCII vowels removed. For example: >>> strip_vowels("Hello, world") "Hll, wrld"  3. Write a Python function that randomly permutes a string. By random we mean that each time you call the function for a given argument all possible permutations are equally likely (note that "random" is not the same as "arbitrary.") For example: >>> scramble("Hello, world") "w,dlroH elol"  4. Write a Python generator function that yields powers of two starting at 1 and going up to some limit. For example: >>> for x in powers_of_two(70): print x 1 2 4 8 16 32 64  5. Write a Python generator function that yields powers of an arbitrary base starting at exponent 0 and going up to some limit. For example: >>> for x in powers(3, 400): print x 1 3 9 27 81 243  6. Write a function that interleaves two lists. If the lists do not have the same length, the elements of the longer list should end up at the end of the result list. For example: >>> interleave(["a", "b"], [1, 2, True, None]) ["a", 1, "b", 2, True, None]  7. Write a function that doubles up each item in an list. For example: >>> stutter([5,4,[3],9]) [5,5,4,4,[3],[3],9,9]  8. Write a Python script (in the file prefixes.py) that writes successive prefixes of its first input argument, one per line, starting with the first prefix, which is zero characters long. For example: $ python prefixes.py vanrossum

v
va
van
vanr
vanro
vanros
vanross
vanrossu
vanrossum

9. Write a Python script (in the file lines.py) that reports the number of non-blank, non-fully-commented lines in the file named by the first argument. Blank lines are those that have either no characters or consist entirely of whitespace; commented lines are those whose first non-whitespace character is #. For example, if the file states.txt has 50 non-blank, non-comment lines:
\$ python lines.py states.txt
50
`