CMSI 386
Final Exam

The test is open-everything with the sole limitation that you neither solicit nor give help while the exam is in progress.

Instructions: Do all the problems on this sheet of paper. These are all short answer or short-code-snippet. Do not over-do things. Many of these problems require that you explain concepts in English. Please be ***brief*** and use ***precise terminology***. Clarity matters a great deal. Also note, I am trying to assess what you know: NOT what you can come up with given hours and hours and hours and hours of time trying things out on a computer. That is what your homework grade measures! Exams allow assessment of “Are you a good computer scientist? Can you think on your feet? Are you comfortable with terminology? Can you express yourself in English, and in code? Can you talk the talk? Can you express the big pictures and the little ones without a crutch?” The best grades go to those who excel at both in long-due-date homework assignments and in tell-me-what-you-already-know tests.

ProblemYou gotOut of
10
10
10
10
10
10
10
10
10
10 10
TOTAL 100
  1. Do the following in JavaScript, Python, Java, C++, and Haskell. Write a function expression that does curried multiplication. For example, your expression e should be such that e(8)(3) would produce 24. By function expression I mean an “anonymous function.” Don’t give a function declaration; give the expression. (Okay, here it is in Python: lambda x: lambda y: x * y. Just do the other four. One line each please.)
    1. Why is it impossible to have a function in Haskell that accepts an arbitrary list and returns a tuple containing all of the list elements in the given order? And why is it impossible to have a function in Haskell that does the reverse (arbitary tuple to list)? (Please be brief: it’s the same reason for both parts!)

    2. Write an Haskell function called toList3 that accepts a three-element tuple and produces the corresponding three-element list (it‘s trivial). Include the type signature. Use pattern matching.
  2. Rewrite this old-fashioned JavaScript function in modern JavaScript:
    function zip3(point, array) {
      return [[point.x, array[0]], [point.y, array[1]], [point.z, array[2]]];
    }
    
  3. Java rejects pattern matching of function parameters. Java also rejects defaults for parameters. (a) Briefly, in one sentence only, why are the Java desginers cramping your style in these two areas? (It is the same reason for both.) (b) What are you supposed to use instead of default parameters?
  4. (Either put a check mark next to “Yes” or fill in the “No” part.) Is a null value a member of:

    1. The JavaScript type "string"? Yes_________ No, but it's a member of __________________________
    2. The Python type str? Yes_________ No, but it's a member of __________________________
    3. The Java type String? Yes_________ No, but it's a member of __________________________
    4. The C++ type string? Yes_________ No, but it's a member of __________________________
    5. The Haskell type [Char]? Yes_________ No, but it's a member of __________________________
  5. In JShell, enter the three commands
    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?

  6. Feeling good about pointers? Assume the existence of the following C++ classes:
    #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;
    
    1. Circle the allowable assignments in C++:
      a = d;
      ap = &d;
      d = a;
      dp = &a
      dp = (Dog*)(&a);
      dp = (Dog*)cp;
      ap = new Dog;
      ap = &(new Cat)
      d = Dog(a).
    2. Is the assignment animals = dogs okay? Why or why not? Be clear. There is something that needs to be said to get full credit.
  7. The problem “produce the index of the first all-zero row in a matrix, or -1 if no such row exists” has an interesting history. It was mentioned in a 1974 paper as a problem for which using a goto statement was suitable. But this isn’t true! We can create lovely one-liners in Python and JavaScript:
    # Python
    next((i for i,row in enumerate(a) if all(x == 0 for x in row)), -1)
    
    // JavaScript
    a.findIndex(row => row.every(x => x === 0))
    
    // C++: okay sorry haha, not exactly a one-liner but no kidding this is it:
    distance(m.begin(), find_if(m.begin(), m.end(), [](const auto& row){
      return all_of(row.begin(), row.end(), [](int x){return x==0;});
    }))
    
    Give the equivalent expressions in Java (you will need streams) and Haskell.
  8. Some languages don’t have loops; they make you use recursion. So, a good programmer should be fluent in translating a function with loops into a recursive one. Here’s an iterative function written in C++ (it’s one of Dr. Dorin’s favorite sequences, I hope you recognize it!):
    int c(int n) {
      int steps = 0;
      while (n > 1) {
        n = n % 2 == 0 ? n / 2 : 3 * n + 1;
        steps++;
      }
      return steps;
    }
    
    When written recursively, the function body can reduced to a single return statement (Did I mention I like Code Golf?). Express this function as a one-liner in JavaScript, Python, Java, C++, and Haskell. (You do not have to write it in a tail-recursive fashion.) (Hint: the recursive formulation should be a single return with a conditional (“ternary”) expression. I think understanding this straightforward recursion pattern is fair game for computer science juniors. In Haskell you can use the if-then-else expression but guards are preferred.)
  9. Remember Data Structures? A binary tree is either empty or a node with some data and two subtrees. Let’s get started. In Python:
    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()
    
    In Haskell:
    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
    
    Add (a) an “inorder” method to the Python class and (b) an “inorder” function to the Haskell program. The function should return a list of each of the node data values according to the tree’s inorder traversal.
  10. EXTRA CREDIT. (a) Explain why C++ move constructors and move assignment operators only make sense on r-values and not l-values. You can use a rough code fragment in your explanation. (b) Why does C++ even have moves, anyway? I mean, we used to have the Rule of 3 and now we have this Rule of 5? Why?