CSC 123/252 Study Guide for the Final Exam. The final exam will mostly focus on the material covered after the midterm but may include some questions on earlier subjects (when does dynamic dispatch occur). Writing code fragments in these languages will be required. ==Topics: Generics (templates): use of 'object' versus generic types (like ) Covariance and Contravariance in C#/Java, including covariant arrays. Differences in implementation of generics in Java, C# and C++ F# programming, including: understanding how types are handled (polymorphic inference, static safety) Discriminated union type definitions (as in type expr = |... |...) pattern matching writing some code fragments Features will reflect those emphasized in class, used in ASSIGNMENTS **Understand the relationship between F# pattern matching and dynamic dispatch and the visitor design pattern. Rust: Understand the rules of ownership, lifetime, and borrowing Study the what-you-need-to-know program and be prepared to answer questions about Rust programs (but you won't have to write Rust code). AOP/AspectJ programming (lightly covered) pointcut expressions (call/execute, etc...) writing advice intertype declarations and other technical features emphasized in class. Understand AOP concepts/keywords including "advice", "cross-cutting", "join point", "inter-type declarations", and "weaving" Contrast AspectJ with attempts to implement AOP features in Perl, F#. For example, how dynamic scoping is useful in simulating the effects of advice on programs. What features are still missing in these languages that AspectJ provides (extension methods, changing types). Another question on the exam will be to see if you understand the relative tradeoffs between the languages. This topic was covered extensively using the different EXPRESSION TREE PROGRAMS. ---------------------------------------------------------------------------- How to Study: Do the sample problems here without looking at the answer. Do NOT memorize definitions or simply learn the mechanics without understanding their purpose. Study previous programming assignments. ---- Sample questions to prepare for the final exam ---- Answers posted separately 1. Compare the following implementations of polymorphic linked lists: class list { object head; list tail; } class list { A head; list tail; } What are the advantages of using one form instead of another? 2. Given the F# definition of a polymorphic linked list structure: type 'a llist = NIL | Cons of ('a*'a llist); What would be the closest equivalent definition in C#. Defend your answer. 3. Describe what will happen with the following C# segments of code, in terms of any compiler errors, or runtime errors, and EXPLAIN WHY // assume using System; an using System.Collections.Generic; a. object[] A = new string[10]; A[0] = 1; // 1 is not a string but it's an object b. List B = new List(); // equivalent to ArrayList in java 4. Write a function in F# to compute n! (e.g., 4! = 4*3*2*1 = 24; 0!=1). You must use pattern matching and tail-recursion (no if-else, no loops). 5. Lists in F# are written as [a;b;c], which is the same as (a::b::c::[]) Write a function to return the length of the list, without using "if" or any built-in .Net functions (you can also write it for the 'a llist structure above. 5b. What would be the type inferred by F# for your function? 6. Assume that F# inferred that the type of a function is ('a -> int). What does this mean with respect to polymorphism? What is an example of a function that will have such a type? 6c. Given the following type for expression trees: type expr = Val of int | Plus of int*int | Times of int*int | Neg of int;; Write an F# expression that represents 4+2*-3. Write a F# function to print a expr expression in infix form. But print (A + -B) as just (A - B) ----- Sample AspectJ problems: 7. Assume that classes C and D both have a function void f(int). Write a pointcut expression that picks out calls to either function. Also capture the argument passed. 8. Given class: class B { private int x; pubilc B(int x0) { x=x0; } // constructor public int f(int n) { if (n<2) return 1; else return n*f(n-1); } public void g(int y) { System.out.println(x+f(y)); } } a. Write a pointcut that picks out the "execution" of the constructor of B. (when the constructor is called, the object doesn't exist yet). b. Write a pointcut that picks out the initial call to f (as opposed to recursive calls). c. The following pointcut and advice tries to change the parameter passed to g. Explain what's wrong with it the way it's written. DON'T JUST CORRECT IT; *EXPLAIN* WHY IT'S WRONG THE WAY IT IS! before(int y) : call(void B.g(int)) && args(y) { g(y+1); } (hint: there are three problems that need to be addressed). d. Write an advice that throws an error if the g function is called from anywhere except main (public static void *.main(..)) f. Suppose an aspect contains the following intertype declarations: Aspect importantaspect { public int B.y; private int B.z; ... Explain the difference between public and private above (hint: they're not the same as public/private within an ordinary class). That is, what are the consequences with respect to other parts of the program. 9. Explain the difference between "withincode" and "cflow" pointcuts. 10. Explain the difference between the "this" and "target" pointcuts. The best way to answer this question is by using a specific example: ----- more questions... 11. (hard) Given type expr = Var of string | Plus of (expr*expr) | Times of (expr*expr);; let sample = Plus(Times(Var("a"),Var("b")),Times(Var("a"),Var("c"))); // sample is (a*b)+(a*c) Write a F# program to factor (a*b)+(a*c) into a*(b+c). You need to do this recursively and factor all expressions inside-out. The following tostring function can be used to test your function: let rec tostring = function | Var(s) -> s | Plus(a,b) -> "(" + tostring(a) + " + " + tostring(b) + ")" | Times(a,b) -> "(" + tostring(a) + " * " + tostring(b) + ")";; // + can also be used for string concatenation in F# printf "before factoring: %s\n" (tostring(sample)); Please note that F# does not allow you to use the same variable twice in a pattern: match x with | Plus(a,a) -> // is not ok match x with | Plus(a,b) when a=b -> is ok. 11b. How would you write this program in C#? What advantages (in general) does F# have over the dynamic dispatch/oop approach? 12. Explain in your own words one advantage of parametric polymorphism over inheritance polymorphism. That is, what's the difference between using a type parameter , and calling your variables (for example) "A x" instead of "object x". 13. Consider the F# definition of expression trees in problem 6c. What is one *disadvantage* of the F# approach that can be addressed with AspectJ. 14. Write a tail-recursive version of the following function in F#, use pattern matching (no if-else allowed) let rec length = function | [] -> 0 | (car::cdr) -> 1 + length(cdr);; 1b. What are the types that will be inferred for these function by the F# compiler? 15. Determine if the following program will compile and run, and EXPLAIN WHY. using System; interface I { int f(T other); } class A : I { protected int x = 2; public int f(A other) { return x-other.x; } } class B : A { double y; public B(double z) {y=z;} } public class hardquestion { public static void Main() { I x = new B(2.5); } } 15b. If it doesn't work, what minimal changes, without changing main, should be made to get the program to work as intended. 16. Determine if the following Rust program will compile. If not, EXPLAIN WHY: fn main() { let x = vec![1,3,5]; let rx = &x; let y = x; let ry = rx; for i in ry { println!("{} ",i); } } How about: fn main() { let mut x = vec![1,3,5]; { let rx = &mut x; rx[0] += 1; } let rx = &x; for i in rx { println!("{} ",i); } }