Here are some long-form exercises for you to work on to improve understanding and retention of course content. Short-form recall questions, designed to be part of your spaced-repetition learning regimen, can be found at the bottom of each page of course notes.
Additional recall questions tied to specific programming languages can be found at the Programming Languages Explorations companion website.
Here are some questions that might be found on an in-class paper-and-pencil exam.
nonlocal (or global) keyword to do this, while JavaScript does not.let x = 3; let x = 3;
Identifier 'x' has already been declared). In Rust, two variables are declared, with the second shadowing the first. You do get a warning though, saying the first variable is never used, but there is no error.switch statement? times method on integers, while C-like languages require a loop with a counter that you don’t care about!Array but in other languages like Java, Rust, and Swift there exist many parameterized array types?x ? y : z of a typical statically-typed language, what type checking and inference rules would a compiler be required to enforce? x must be boolean and the types of y and z must be compatible. Inference: the type of the entire expression is the least general type of both y and z.def f(): yield 1; yield 2; yield 3, what is wrong with writing for i in f: print(i)? What should we write instead?len("Hello, 世界") == 9, but in Rust, "Hello, 世界".len() == 13. Why is this? x = x for example, you have to early exit before destroying anything.std::fill, that fills the first 100 elements of the C-style array a of integers with 0s. std::fill(a, a+100, 0)Object.setPrototypeOf(myObject, theNewPrototype)Promise.all(arrayOfPromises).then(callback)TypeError is thrown, such as (1) trying to access a property of null or undefined, (2) trying to call a non-function value, (3) trying to use a non-iterable value in a for-of loop.int, float, and double have fixed bit sizes? If so, what are they? If not, why not? list(t), for some tuple t, produces the list containing the elements of t in order. Can you write a function in Haskell to do the same thing, i.e., build a list from some arbitrary tuple? If so, give the Haskell function that does it. If not, explain why Haskell can’t do this. [1,12] < [1,3] true but [1,42] < [1,3] false? Why are both expressions false in Python and Haskell? < turns arrays into strings before comparing, and "1,12" < "1,3". Python and Haskell do numeric element-by-element comparison and 1=1 and 12 > 3.Stream.of(1,2,3).collect(Collectors.averagingInt(f)) for some particular function f. What should f be here? x -> x or Integer::newIntStream.range(1, n+1).reduce((x, y) -> x * y) is a good attempt to produce the factorial of n, but it doesn’t produce an integer. What does it produce? How can you modify it (slightly) to produce the integer factorial? reduce, passing in 1 as the first argument. You can also do an .orElse(1) at the end.r was a (Java) Reader object. How do you get the lowercased lines of the reader into an array, eliminating all duplicates? r.lines().distinct().map(String::toLowerCase).toArray(String[]::new)if-let, while-let, or guard-else constructs required to access an element of an optional array? If not, what is a more concise way to access the element? a and integer i, you get the convenient a?[i].indirect enumeration, using the definition “A list is either empty or a value (called the head) connected to a list (called the tail).” (Just define the type, you don’t need to include any methods.) indirect enum MyList<T> {
case empty
case node(head: T, tail: MyList)
}"string", str, String, std::string, String, [Char]), or if not, what related type is it a member of? For example, if you were asked about Swift, you would say: “nil is not a member of the type String, but it is a member of String?”. null ∈ the Null type.None ∈ NoneType.nullptr is not in the std::string type, but ∈ char* and std::string*.Nothing ∈ Maybe String.When written recursively, the function body can reduced to a single expression! Express this function as a one-liner in JavaScript, Python, Java, C++, Swift, Kotlin, or Haskell: your choice! If using Haskell, use guards rather than anint c(int n) { int steps = 0; while (n > 1) { n = n % 2 == 0 ? n / 2 : 3 * n + 1; steps++; } return steps; }
if expression.
function c(n) { return n<=1 ? 0 : n%2===0 ? 1+c(n/2) : 1+c(3*n+1) }def c(n): return 0 if n<=1 else 1+c(n//2) if n%2==0 else 1+c(3*n+1)static int c(int n) { return n<=1 ? 0 : n%2==0 ? 1+c(n/2) : 1+c(3*n+1); }int c(int n) { return n<=1 ? 0 : n%2==0 ? 1+c(n/2) : 1+c(3*n+1); }func c(n: Int) -> Int { return n<=1 ? 0 : n%2==0 ? 1+c(n/2) : 1+c(3*n+1); }fun c(n: Int): Int { return n<=1 ? 0 : n%2==0 ? 1+c(n/2) : 1+c(3*n+1); }c n | n<=1 = 0 | even n = 1+c(n `div` 2) | otherwise = 1+c(3*n+1)The tail-recursive way is better. Left as an exercise to you.
a = [10,20,30,30,40,30], draw a picture of the world after b = delete 30 a that shows you understand persistent data structures. (delete is from Data.List).
a ------> 10 ----> 20 ----> 30 ----> 30 ----> 40 ----> 30
^
|
b ------> 10 ----> 20 ---------------+
(/ 2) and (/) 2 different functions, but (elem 21) and (elem) 21 are the same function? / is an infix operator, (/ 2) is a right operator section which divides its argument by 2, but (/) 2 is a function which returns 2 divided by its argument! Alternatively, since elem is a regular prefix operator, there is no section here, and (elem 2) and (elem) 2 are just putting parentheses where they already belong anyway.split to break a string into a list of characters, and join to turn an array of characters into a string. How do you do these two operations in Swift? Array(s) breaks a string into a list of chars, String(a) joins a list of strings into a single string.tsc a compiler or a transpiler? Do we care?
Exp7 = "-" Exp8
| Exp8 "**" Exp7
| Exp8
Exp, Term, Factor, and Primary that make the expression -2**2:
(-2)**2 and -(2**2) to be legal.sqrt into a standard library (as opposed to being wired into the language, or left to a “third party” library? WhileStmt = "while" Exp BlockHow do we fix this problem?
while = "while" ~idrest and redefine the while statement rule as WhileStmt = while Exp Block."//" (~"\n" any)* "\n"
Factor = "-" Primary -- negation
| Primary
is actually an abbreviation for two separate rules. Give those two rules.
Factor = Factor_negation
| Primary
Factor_negation = "-" Primary
Exp = Term ("+" Term)*
fails to capture what aspect of the + operator in the syntax? A ~B matches an A that is not followed by a B. How do we match an A that is followed by a B (without consuming the B)? &(A B) A
A = A "+" A --plus
| "a"
which looks ambiguous. Ohm actually makes the operator here be right associative, so given the fact that Ohm is an implementation and always parses strings exactly one way, then no, its grammars are not ambiguous; however, this isn’t really specified anywhere so maybe yeah, that grammar in some sense is ambiguous. See issues 55 and 56 for more information.
E = E "•" T --binary
| T
E = T "•" E --binary
| T
E = T "•" T
E = E binaryop "(" E ")"
| num
a<b<c. Some people think it should be automatically expanded to a<b && b<c. Others think it should be (a<b)<c.console.log(() => {if (true) return;})x < y < z. So what exactly happens when the compiler encounters this code fragment (assuming all variables are in scope)? x < y is checked. That is either a type error or a perfectly valid comparison assigned the type boolean. Booleans can’t be compared anyway, so the entire expression is always a type error, detectable at compile time! But it is NOT a syntax error.x ? y : z of a typical statically-typed language, what type checking and inference rules would a compiler be required to enforce? x must be boolean and the types of y and z must be compatible. Inference: the type of the entire expression is the least general type of both y and z.Here are some problems that require some thinking, and some actual work. They may involve writing little scripts, or making sketches. They aren’t exactly short-answer problems.
(-b + sqrt(4 × a × c)) / (2 × a)Do you need a special symbol for unary negation? Why or why not?
function f() { return { x: 5 } }
let b = 8 let a = b + b (4 + 5).toString(16)
let place = "mundo" ["Hola", "Ciao"].forEach((command) => { console.log(command + ", " + place) })
What is being illustrated in each of the above? Go, Python, Scala, and Ruby all allow line endings to end statements and you don’t hear people complaining about them the way they do about JavaScript. Pick one of these four languages and show why they don’t have problems with these four cases.const sayHello = function () { console.log("Hello") } (function() { console.log("Goodbye") }())
(* 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, in one popular textbook (Programming Language Pragmatics by Michael Scott), does it say “Issues of precedence and associativity do not arise with prefix or postfix notation”? Reword this claim to make explicit the hidden assumption. f(2) should return 2, but if local variables were allocated statically, it would return 3.
void f(int x) {
int a = 2;
int b;
if (x < 0) {
a = 3;
return 0;
} else {
b = f(-x);
return b + a;
}
}
declare x = 2;
sub f() {print x;}
sub g() {declare x = 5; f(); print x;}
g();
print x;
var x = 1
function f() {return x;}
function g() {var x = 2; return f();}
print g() + x
var x = 1
function h() {var x = 9; return g();}
function f() {return x;}
function g() {var x = 3; return f();}
print f() * h() - x
var x = 1
function h() {var x = 0; return g()}
function f() {return x}
function g() {var x = 8; return f()}
print f() - h() + x
var x = 100;
function setX(n) {x = n;}
function printX() {console.log(x);}
function first() {setX(1); printX();}
function second() {var x; setX(2); printX();}
setX(0);
first();
printX();
second();
printX();
function g(h) {
var x = 2;
h()
}
function main() {
var x = 5
function f() {
print x + 3
}
g(f)
}
main()
function f(a) {
var x = a - 1
function g() {
print x - 17
}
h(g)
}
function h(p) {
var x = 13
p()
}
f(18)
public void fail() { class Failure extends RuntimeException {} throw new Failure(); }
function g(h) { var x = 2; h() } function main() { var x = 5 function f() { print x + 3 } g(f) } main()
function f(a) { let x = a - 1 function g() { print x - 17 } h(g) } function h(p) { let x = 13 p() } f(18)
x = 3 def f(): print x def g(): x = 5 f() g()
x = 3 def f(); puts x; end def g(); x = 5; f(); end g()
(where global declarations ofX: Integer := X + 1; T: T; Y: Real := Y(T);
X, T and T 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.
public void fail() {
class Failure extends RuntimeException {}
throw new Failure();
}
std::array and one with std::vector, and use std::fill if appropriate.IntStream.range(1, n).reduce((x, y) -> x * y) is a good attempt to produce the factorial of n, but it doesn’t produce an integer. What does it produce? How can you modify it (slightly) to produce the integer factorial?| Operator(s) | Assoc | Arity | Fixity |
|---|---|---|---|
| § # | 1 | Prefix | |
| ∀ ⊥ | 1 | Postfix | |
| % • ¥ | L | 2 | Infix |
| ⊗ ∇ ⇒ | R | 2 | Infix |
# # A ⇒ # B ∇ C ¥ D % E ∇ F % G • H ⊥ ∀ ⊗ § I ∀
⇒
/ \
# ∇
| / \
# # ∇
| | / \
A B % ⊗
/ \ / \
¥ E • ∀
/ \ / \ |
C D % ∀ §
/ \ | |
F G ⊥ I
|
H
a–f(b)–c*d can produce different values depending on how a compiler decides to order, or even parallelize operations. Give a small program in the language of your choice (or even one of your own design) that would produce different values for this expression for different evaluation orders. Please note that by "different evaluation orders" we do not mean that the compiler can violate the precedence and associativity rules of the language.TypeError. What are the cases in which this error is thrown?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 requires dynamic type checking. Include in your answer a well-written explanation that shows you truly understand the difference between static and dynamic types. Dog and Rat are subclasses of Animal, this code
compiles fine but when executed throws anAnimal[] pets = new Dog[4]; pets[0] = new Rat();
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.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;
c within sortsplitmergey in the third clause of split?sort and explain why it is not what you would expect.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 type check an expression in which an A was used as a B? What problem might you run into?If the address ofstruct { int n; char c; } A[10][10];
A[0][0] is 1000 (decimal), what is the
address of A[3][7]?double *a[n]; double (*b)[n]; double (*c[n])(); double (*d())[n];
double *a[n]; double (*b)[n]; double (*c[n])(); double (*d())[n];
"string"? Yes_________ No, but it's a member of __________________________
str? Yes_________ No, but it's a member of __________________________
String? Yes_________ No, but it's a member of __________________________
string? Yes_________ No, but it's a member of __________________________
[Char]? Yes_________ No, but it's a member of __________________________
String[] x = new String[]{"A", "B"}
Object[] y = x
y[1] = 50
Omg, Java just gave a run-time error! So can you say “Java is a statically-typed language?” If so, why? If not, what is a better thing to say?Describe rigorously, in English, the type ofdouble (*f(double (*)(double, double[]), double)) (double, ...);
f.| To→ From↴ | Bool | Num | Str | Arr | Dict | Fun |
|---|---|---|---|---|---|---|
| Null | false | 0 | "" | [] | {} | |
| Bool | false→0 true→1 | |||||
| Num | 0→false else true | |||||
| Str | ""→false else true | |||||
| Arr | []→false else true | |||||
| Dict | {}→false else true | |||||
| Fun | true |
{"a":"b", "c":"d"} ⇒ ["a","b","c","d"].
["a","b","c","d"] ⇒ {"a":"b", "c":"d"}.
x, we have *x == x. If this is not possible, state why it is not possible.X, we have X^ = X in Modula 3 or X.all = X (in Ada). If this is not possible, state why it is not possible.(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?#include <list> class Animal {}; class Dog: public Animal {}; class Cat: public Animal {}; Animal a; Animal* ap; Dog d; Dog* dp; Cat c; Cat* cp; list<Animal> animals; list<Dog> dogs;
a = d;ap = &d;d = a;dp = &adp = (Dog*)(&a);dp = (Dog*)cp;ap = new Dog;ap = &(new Cat)d = Dog(a)
animals = dogs okay? Why or why not? Be clear. There is something that needs to be said to get full credit.
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 mid-test 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 mid-test version?
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.
(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()).
The intent of the code is to setint first_zero_row = -1; /* assume no such row */ int i, j; for (i = 0; i < n; i++) { /* for each row */ for (j = 0; j < n; j++) { /* for each entry in the row */ if (A[i][j]) goto next; /* if non-zero go on to the next row */ } first_zero_row = i; /* went all through the row, you got it! */ break; /* get out of the whole thing */ next: ; } /* first_zero_row is now set */
first_zero_row to the index of the first all-zero row, if any, of an n × n matrix, or -1 if no such row exists. Do you find the example convincing? Is there a good structured alternative in C? In any language? Give answers in the form of a short essay. Include a good introductory section, a background section describing views on the goto statement throughout history, a beefy section analyzing alternatives to Rubin's problem, and a good concluding section. Talk about solutions in at least three languages. You may find higher order functions such as every, some, and forEach to be helpful.function indexOfFirstAllZeroRow (a) { return a.map(row => row.every(x => x === 0)).indexOf(true) }
function indexOfFirstAllZeroRow (a) { for (let i = 0; i < a.length; i++) { if (a[i].every(x => x === 0)) { return i; } } return -1; }
However, findIndex short circuits!
function indexOfFirstAllZeroRow (a) { return a.findIndex(row => row.every(x => x === 0)) }
function firstAllZeroRow (a) { let result = -1 a.some((row, i) => { return row.every(x => x === 0) ? ((result = i), true) : false }) return result }
result within the some callback, which can be confusing. A more straightforward approach would be to directly use findIndex, as shown in the previous answer.def first_all_zero_row(a): return next((i for i,row in enumerate(a) if all(x == 0 for x in row)), -1)
function zip3(point, array) { return [[point.x, array[0]], [point.y, array[1]], [point.z, array[2]]]; }
lambda x: lambda y: x * y. Just do the other four. One line each please.)toList3 that accepts a three-element tuple and produces the corresponding three-element list (it‘s trivial). Include the type signature. Use pattern matching.A possible Ruby solution is:function ss(a) { function s(i, acc) { return i == a.length ? acc : s(i+1, a[i]*a[i]+acc) } return s(0, 0); }
def ss(a) s = Proc.new{|i,acc| i==a.length ? acc : s[i+1, a[i]*a[i]+acc]} s[0, 0] end
i of the array contains a function (or pointer to a function if your language demands it) that divides its argument by the square of i.withoutEvens([4,6,true,null,2.3,"xyz"]) => [6,null,"xyz"]
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;
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; }
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$.
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).
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.The transpose function should work like thisval dot = let fun transpose ... = in .... end;
raisingtranspose ([x1,...,xn],[y1,..,yn]) = [(x1,y1),...,(xn,yn)]
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.
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 2 3 4.
2 2 2 4.
y[1], and that becomes 2 + 1 = 3, so it prints
2 2 3 4.
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.
x = 1
y = [2, 3, 4]
function f(a, b) {a--; b = x - 1}
f(x, y[x])
print x, y[0], y[1], y[2]
x = 1;
y = 2;
function f(a, b) {a = 3; print b, x;}
f(x, x + y);
print x;
Local variablevoid do_something() { int i; printf("%d ", i++); } int main() { int j; for (j = 1; j <= 10; j++) do_something(); }
i in subroutine do_something 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.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;
call f(2) print* 2 stop end subroutine f(x) x = x + 1 return end
nextOdd (or next_odd or nextodd depending on the language’s culture). 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?"MI", (2) a function which retrieves the value of this string, and (3) four other methods, each of which changes the string to another string according to the following:
"I", add "U", e.g. "MI" → "MIU".
"MIIU" → "MIIUIIU".
"III" with "U", e.g. "MUIIIU" → "MUUU".
"UU", e.g. "MIUUUI" → "MIUI".
const mu = (() => { let data = "MI" return { value: function () { return data }, rule1: function () { if (/I$/).test(data) data += "U" }, rule2: function () { data = data + data.slice(1) }, rule3: function () { data = data.replace(/III/, "U") }, rule4: function () { data = data.replace(/UU/, "") } } })()
m, s, or y?
Container (here using C++ syntax):
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?
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?Person and subclasses for different jobs (e.g., programmer, manager, ticket agent, flight attendant, supervisor, student, etc.) is a lousy design. Give an alternative.dict, indexed by semester name, whose values are dicts mapping course numbers to grades. Supply methods to read the id, read and write the name, read and write the birthday, get the grade for a given course (in any semester), and add an item to the transcript. Did you encapsulate (i.e. hide) the properties inside the class so they were protected from direct access from outside the class? Why or why not?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.)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).
and there is no compile-time nor link time error, despite the fact thatclass C {int x;}; C c; C* p = &c; cout << p;
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.Does the representation of aclass Base { public int a; public String b; } class Derived extends Base { public float c; public int b; }
Derived object contain one b field or two? If two, are both accessible, or only one? Under what circumstances? Answer for C++, Java, Python, Kotlin, Scala, or any similar language.Dog is an abstract class in a C++ program, why is it acceptable to declare variables of type Dog*, but not of type Dog? Does this problem even make sense to ask in Java or Python?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.E of task T only if T has not terminated.
if not T'Terminated then T.E; end if;
let countDown = function () {
let i = 10;
let update = function () {
document.getElementById("t").innerHTML = i;
if (i-- > 0) setTimeout(update, 1000);
}
update();
}
The second argues that this is better:
let countDown = function () {
let 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.
IDLIST → ID ^ ","
This form can also be used to model a construct representing one or more $A$s, rather than using $AA^*$ or $A^*A$. Show how to do this.
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
comment = "--" ~"\n" any
and and or?
X and Y or Z. (Assume, of course, that an Exp5 can lead to identifiers and numbers, etc.) If this is not possible, prove that it is not possible.
not operator right associative? Why or why not?
-8 * 5.
- Exp5 to Exp4. Give the abstract syntax tree for the expression -8 * 5 according to the new grammar.
Suppose I wanted to add a new one:
Show how to write $A \# B \# C$ using only the conventional EBNF markup.
"oo" (or "oO" or "Oo" or "OO").
"oo".
IfStmt = "if" "(" Exp ")" Block
("else" "if" "(" Exp ")" Block)*
("else" Block)?
Block = "{" STMT* "}"
What if we tried the same approach in a language with a syntax like Ruby (or Fortran or Modula — languages using a terminating end)? We might get a grammar like this:
IfStmt = "if" Exp "then" STMT+
("else" "if" Exp "then" STMT+)*
("else" STMT+)?
"end"
Is this grammar left recursive? Is it $LL(k)$? Why or why not? Is this bad?
A → B C
B → a | b?c?
C → c | BA
If this grammar is not $LL$, make one that is (that defines the same language of course). Give a set of syntax diagrams for the original diagram, and if another is needed, for the new grammar as well.
S = A M
M = S?
A = "a" E | "b" A A
E = ("a" B | "b" A)?
B = "b" E | "a" B B
"abaa"
$ \begin{array}{lcl} \mathit{Exp} & \longrightarrow & \textit{id}\;\texttt{":="}\;\textit{Exp} \;|\; \mathit{Term}\;\mathit{TermTail}\\ \mathit{Term} & \longrightarrow & \mathit{Factor}\;\mathit{FactorTail}\\ \mathit{TermTail} & \longrightarrow & (\texttt{"+"}\;\mathit{Term}\;\mathit{TermTail})? \\ \mathit{FactorTail} & \longrightarrow & (\texttt{"*"}\;\mathit{Factor} \mathit{FactorTail})? \\ \mathit{Factor} & \longrightarrow & \texttt{"("}\;\mathit{Exp}\;\texttt{")"} \;|\; \textit{id} \end{array} $
Exp expand to a string beginning with id.while, followed by a test expression, followed by a block (a curly-brace delimited sequence of statements). The if-statement should start with the keyword if, followed by a test expression, then a block, then an optional else-part which is the keyword else followed by either a block or another if-statement. Neither the while statement nor the if statement should end with a semicolon. The break statement is only allowed to appear in a while statement’s block. The relational operators are the same as those in Python and are to be NON-associative. All of the relational operators are on the same precedence level, lower than all other operators. Give the syntax of Astro++ using Ohm. Hint: Check your grammar with the Ohm Editor so that you don’t needlessly throw away points.
"oo". Do this by matching against a regular expression.[01]*(10111[01] | 11[01][01][01][01])[01]*
([bc]*a[bc]*a[bc]*)*
0*1 | 0*10
c*a[ac]*b[abc]*
"return" nor "retry"
"exit" nor "exec"
"exit" nor "exec"
\b for word boundaries) that are preceded by the word “the”.4 + 15 digits or 4 + 12 digits.
51-55 + 14 digits or 2221-2720 + 12 digits.
python, pycharm, or pyc.
X, make it so that X^ = X. If this is not possible, state why it is not possible.X, make it so that X.all = X. If this is not possible, state why it is not possible.x of type x such that x.x = x, or state why this is impossible.(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?continue statement of C.
type Real_To_Real is access function (Real) return Real; type Foo is access procedure (Integer; in out Boolean); Sine, Cosine: Real_To_Real; P: Foo; Q: Real_To_Real; function Integrate (F: Real_To_Real; A, B: Real); ... function Square (X: Real) return Real is begin return X * X; end; ... Put (Integrate(Square'Access, 3, 10)); Q := Cosine; if Q(Pi) > X then ...
Describe the semantic rules relating to this facility in Ada, and how you would enforce them in a compiler.
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.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.
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.
DESIGNATOR → DESIGNATOR "." ID
for specifying variables made up from a record and a field of the record. But sometimes it can have the additional interpretation that the DESIGNATOR to the left of the dot was the name of a (visible) subprogram and the ID was an object declared immediately inside that subprogram. Show how to rearchitect the entity class hierarchy to support this.let x = 3;
let x = 3;
private. You can use an AI assistant for help, but please trim down the long-winded applications those tools are known for, and give a concise explanation that proves you truly understand the difference.function countDownFrom10() { let i = 10; function update() { document.getElementById("t").innerHTML = i; if (i-- > 0) setTimeout(update, 1000); } update(); }
Your other friend says “Yikes, you are updating a non-local variable! Here is a better way:”
What does your second friend’s function do when called? Why does it fail? Your friend is on the right path though. Fix their code and explain why your fix works.function countDownFromTen() { function update(i) { document.getElementById("t").innerHTML = i; if (i-- > 0) setTimeout(update(i), 1000); } update(10); }
let [x, y] = [0, 0]; console.log(93.8 * 2 ** x + y);
import x from "x" console.log(93.8 * {x} << x.r[z])
const x = x / {[x]: `${x}`}[x]("y")
class C { f({a, b: c}) {return ([a,f]) => C} }
let [x, y] = Array.repeat(10|-2, 2); function f({x, y}, ...p) { return q => `"Say ${y/p[0].x} today`; }
static protected synchronized long g(Object... m) { for (int y : f(x)) { x = p.data[0] * (3<< 7|- x---c); } }
for (int i = x-3; q<=4&m.z[r |- 4]&2-8*r>- 5/~x;) { while (a) { y; 2,y; } }
void f(int x,...) { struct e { double x; struct e *c[10]; char* (*f)(); }; struct e p; exit(p.c[1]->f()[6 |~ x+2 >> x]); }
int abc() { return x = 4&x---*&y.m[-9]; }
if (x > 2 || !String.matches(f(x))) { write(-3 * q); } else if (! here || there) { do { while (close) tryHarder(); x = x >>> 3 & 2 * x; } while (false); q[4].g(6) = person.list[2]; } else { throw up; }
package p; class C implements A { public static A x = new t[3]; Socket s () { while (x - 6>p | e || q +- p) { this.x[3] = !v+++t; } } {System.out.println("ooh");} }
(a = 3) >= m >= ! & 4 * ~ 6 || y %= 7 ^ 6 & p
double f(int x, double y) { return 4 * x + y; }
je L6
jmp L4
L6:
with the single instruction jne L4?
X := Y; where X and Y are both 32-bit integers that are one step down the static chain from the current subprogram, by a code generator which emits access code for the two values independently. Assume $X$ is at offset $-8$ and $Y$ is at offset $-12$. How many registers are used? Then generate code for this statement by hand, intelligently.
and happened to have offset $-42$ in the frame of the subprogram in which it was declared. Suppose further that the variabletype array (21..38) of String(1..10)
J was declared in the same subprogram and had offset $-26$.
A(J-1) into register eax that would be generated naïvely. Do not forget to show the bounds checking!
A(J-1) into register eax in which the "-1" computation is folded in to the computation of the base address of A. Note that the bounds checking code will look a little different than in part (a).
mov [ebp-8], eax
mov eax, [ebp-8]
The second instruction might be able to be removed. But whether we are able to remove this instruction is undecidable. Why, exactly?
enter instruction which automatically makes a display. Research this instruction. Suppose a Carlos program had the following structure (indentation determines nesting):
function f, parameters: [x,y], locals: [a]
function g, parameters: [c], locals: [p,q,r,s]
function h, parameters: [a], locals: []
function k, parameters: [], locals: [z]
enter n, 0
enter n, 1
enter n, 2
enter n, 3
...
and so on. The second column will be the number of clock cycles required on a Pentium for the particular ENTER instruction. The third column will be code equivalent to the ENTER instruction. For example, ENTER n, 1 is equivalent to:
push ebp
mov ebp, esp
push ebp
sub esp, n
The fourth column will be the number of clocks for the code in column 3.
x / y > (3 * x) || z || x < 3
where the "||" operator is short-circuit, and the variables $x$, $y$, and $z$ are all integer variables. Put the value of the expression in eax. Write the best possible code you can for the Pentium 4 processor.
LEA instruction for the 3n+1 computation:
int C(int n) {
int count = 0;
while (n != 1) {
n = (n % 2 == 0) ? n / 2 : 3 * n + 1;
}
return count;
}
if (x % 4096 == 0) {printf("Don't say \66;\6f;\6f;!");}
Hint: you need strength reduction, too.
sar eax, 10 to divide by 1024
sar eax, 8 to divide by 256
This optimization is not safe. Explain why. Show how to make it safe, and explain both why your optimization works and why it is safe.
for j := 5 to y do
y := j * 7 + c;
printInteger(y - 4);
end loop;
where y and c are local variables in the current procedure at offsets -12 and +16 respectively. Remember that the range is evaluated only once, the whole loop is skipped on the empty range, etc.). Make sure you respect the overflow semantics! Identify any induction expressions and explain how you optimized them. Compare your hand-written code with that generated by a real compiler.
fyl2x and fldl2t instructions.
double f(double a, double b);
mov eax, n
shl eax, 23
add eax, 3f800000h
mov [esp-4], eax
fld dword [esp-4]
y := x * 4 + z;
z := p * y;
y := z;
x := z / y << x;
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).
Classify the following as a syntax error, static semantic (contextual) error, or not a compile time error. In the case where code is given, assume all identifiers are declared, have the expected type, and are in scope. All items refer to the Java language.
x+++-y
x---+y
null instanceof C
!!x
x > y > zif (a instanceof Dog d) {...}var s = """This is weird""";switch = 200;x = switch (e) {case 1->5; default->8;};x---y
x-----y
x+++-y
x---+y
null instanceof C
!!x
x < y < z in Carlos, where x and y are ints and z is a boolean.
x < y < z in C, where x and y and z are all ints.
3[a] in Carlos, where a is an array variable.
3[a] in C, where a is an array variable.
char x = '\a'; in Carlos.
char x = '\a'; in C.
sin standard function to an integer
x < y < z where $x$ and $y$ are integers, and $z$ is a boolean.
import java.util.HashMap; class C { static final HashMap<String, Integer> m = new HashMap<String, Integer>(); static int zero() { return 0; } public C() { } }
You can use SonarLint or FindBugs or FindSecBugs or PMD or whatever you prefer. You might even need to use a combination of tools because it is possible no tool finds them all. (Please note you are not expected to already know what all the issues are here. The idea is to practice with tools and have good discussions with teammates. Find as many as you can, and read and understand each problem that is reported to you so you learn (1) what kinds of potential bugs and security problems can exist even in compilable and runnable code, and (2) the kinds of things that a static analyzer can detect.)
Keep sharp by practicing your programming skills.
[1,2,3], the output is [1,1,2,2,3,3]. If your language allows adding methods to an array class (or provides for extensions), use that feature.# character.divmod operator, write two solutions, one using the operator and one that does not.fringe that takes a tree as argument and returns an ordered list of its leaves. Then we can say:
However, fully computing the fringes of both trees is extremely inefficient, given that you should be able to return false at the first mismatch, so the fringes should be computed lazily. Write an efficient (lazy) version ofdef same_fringe(t1, t2): return fringe(t1) == fringe(t2)
same_fringe in as many languages as you can.
std:array, and using std::vector.f and g) such that every time you call f, you get back 10 less than the result of the previous call to f or g, and every time you call g, you get back three times the absolute value of the result of the last call to f or g. Here's a catch: you must arrange things so that any "state" you need is completely private to f and g.(* and ending with *) that do not contain *). Note comments "don’t nest".class BinaryTree: pass class Empty(BinaryTree): def size(self): return 0 class Node(BinaryTree): def __init__(self, data, left, right): self.data = data self.left = left self.right = right def size(self): return 1 + self.left.size() + self.right.size()
Implement methods preorder, inorder, postorder, height, and width.
data BinaryTree a = Empty | Node a (BinaryTree a) (BinaryTree a) size: BinaryTree a -> Int size [] = 0 size (Node data left right) = 1 + size left + size right
Implement functions preorder, inorder, postorder, height, and width.
select statements and (2) using a mutex.There are a number of skills beyond just slinging code that will set you apart from peers. These include being able to explain what programs are saying (or doing), being able to explain why certain programs are incorrect or insecure, and being able to explain why certain programs are correct or secure. Here are a few such problems.
a = [lambda: i for i in range(10)] b = [a[i]() for i in range(10)]
At the end of this script, what is the value of b? Explain in detail why this is. Support your analysis with a sketch.
var that you should never use in your own code:
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () {return i;} } var b = []; for (var j = 0; j < 10; j++) { b[j] = a[j](); }
At the end of this script, what is the value of b? Explain in detail why this is. Support your analysis with a sketch.
dog="spike"; class <<dog; def bark = "arf"; end; dog.bark
"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; "sparky".bark
class <<"sparky"; def bark = "woof"; end; "sparky".bark
dog="sparky"; class <<dog; def bark = "woof"; end; dog.bark
"woof", because we attached the bark method
to the object referenced by the variable dog, through
its metaclass.
class <<"sparky"; def bark = "woof"; end; "sparky".bark
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; :sparky.bark
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; 2.bark
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
but a unit test failed. What was this test, and why did it fail?def dot(a, b) a.zip(b).map{|x,y| x*y}.inject{|x,y| x+y} end
assert_equal(dot([], []), 0) and it failed because her
method returned nil, not 0.
but when running a unit test with the first array longer than the second, adef dot(a, b) a.zip(b).inject(0){|x,y| x+y[0]*y[1]} end
TypeError was raised. They fixed by raising an ArgumentError if the arguments to dot had different lengths. Show the 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
And the unit tests worked:class Array def *(a) # working code here end end
[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. Why is this solution so dangerous?
* 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.
class Pair implements Cloneable { private Object first, second; public Pair(Object x, Object y) {first = x; second = y;} public first() {return first;} public second() {return second;} }
L1:
r0 := x
z := 6
r1 := 4 - r0
r2 := 3 >= r1
if r2 == 0 goto L2
r3 := y + 4
r4 := *r3
z := r4
L2:
struct s {int x; int y; string s;}
s a = new s {
codepoint(getChar()), codepoint(getChar()), getString()};
while (a.x++ < a.y) {print($a.s[1]);}
for (int i = 0; i < #a; i++) {
print($a[i]);
print(", ") if (i != #a-1);
}
With optimizations turned off, my compiler produces:
p0: copy 0, i1 L0: copy [i0-4], r0 less i1, r0, r1 jz r1, L1 assert_not_null i0 copy [i0-4], r2 assert_in_range i1, 0, r2 mul i1, 4, r3 add i0, r3, r4 copy [r4], r5 to_string r5, r6 param r6 call __print, 4 copy [i0-4], r7 sub r7, 1, r8 not_equal i1, r8, r9 jz r9, L2 param s0 call __print, 4 L2: inc i1 jump L0 L1: exit s0: [44, 32]
x || y || z under this assumption.
int f(const int n) {
return n % 2 == 0 ? n / 2 : 3 * n + 1;
}
give both a highly optimized WebAssembly translation and a highly optimized x86-64 translation. You do not have to do this by hand; instead, use the Compiler Explorer, and set the optimization to -O3. Include comments in the translated code.
PROGRAM → (DECL ';')* EXPR
DECL → 'val' ID '=' EXPR
| fun ID '(' PARAMS? ')' '=' EXPR
EXPR → NUMLIT | ID | UOP EXPR | EXPR BOP EXPR
| EXPR '?' EXPR ':' EXPR | ID '(' ARGS? ')' | '(' EXPR ')'
PARAMS → ID (',' ID)*
ARGS → EXPR (',' EXPR)*
UOP → '-' | 'abs' | 'not'
BOP → '+' | '-' | '*' | '/' | 'mod' | 'and' | 'or' | '==' | '<'
Exp → Exp Exp op | intlit
op → "+" | "-" | "*" | "/"
G -> (S s G)?
S -> V q e | i f E g | V x
V -> i | V d i | V a E a
E -> n | V
B -> (a|b)*A | bba*c
A -> Ac | d
down deg color 1 0 0 left
90 forward 4 color 0 0 1 [ left 90
forward 1.5 ] right 90 forward 1.5 up
This program draws the letter T with a red vertical line of size 4 units and topped with a 3 unit blue line. A program is a sequence of instructions. The instructions are:
| Instruction | Description |
|---|---|
deg | switch to degree mode |
rad | switch to radians mode |
down | put the pen down so movements draw lines |
up | pick the pen up so movements don't draw anything |
left θ | turn counterclockwise by angle θ
|
right θ | turn clockwise by angle θ
|
forward n | draw a line by moving forward n units. |
backward n | draw a line by moving backward n units. |
color r g b | set color (r,g,b), values are floats in the range 0 to 1. |
[ | save current state |
] | restore previously saved state |
func followed by the function’s name (an identifier), then a parenthesized list of zero or more parameters (also identifiers) separated by commas, then the body, which is a sequence of one or more expressions separated (NOT terminated) by semicolons with the expression sequence terminated with the keyword end. 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’s a conditional expression with the syntax y if x else z. Factorial has the highest precedence, followed by negation, the multiplicative operators, the additive operators, and finally the conditional. 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. String literals delimited with double quotes with the escape sequences \', \", \n, \\, and \u{hhhhhh} where hhhhhh is a sequence of one-to-six hexadecimal digits. Identifiers are non-empty sequences of letters, decimal digits, underscores, at-signs, and dollar signs, beginning with a letter or at-sign, that are not also reserved words. Function calls are formed with an identifier followed by a comma-separated list of expressions bracketed by square brackets. Comments are -- until the end of the line. Write a single example program that covers every aspect of this definition.