Exam 2: Midterm

Solutions and Commentary

Part of the homework for CS:2820, Fall 2017
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

Grade Distributions

Exam 2

              X
Mean   = 4.37 X
Median = 4.0  X
              X
              X
        X     X
        X X X X     X
        X X X X     X     X     X
        X X X X     X   X X     X X
        X X X X     X X X X   X X X X
      X X X X X X X X X X X   X X X X     X
 _____X_X_X_X_X_X_X_X_X_X_X___X_X_X_X_X_X_X___
   0 . 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 10

Exams 1—2

Mean   = 10.05
Median =  9.8
                X                 X
            X   X                 X
            X   X                 X
            X X X     X   X       X
            X X X     X   X   X   X
            X X X X   X X X   X   X
            X X X X X X X X X X   X X X
          X X X X X X X X X X X   X X X
 _________X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_____
   0 . 2 . 4 . 6 . 8 . 10. 12. 14. 16. 18. 20

Machine Problems 1—3

                      X
Mean   = 10.67        X     X
Median = 10.9         X     X
                  X   X     X
                  X   X     X X
                  X   X     X X X
                  X   X     X X X
                  X   X     X X X
                  X X X   X X X X
                X X X X   X X X X
                X X X X X X X X X
                X X X X X X X X X X
 _________X___X_X_X_X_X_X_X_X_X_X_X_
   0 . 2 . 4 . 6 . 8 . 10. 12. 14. 

Relationship between Exams 1—2 and Machine Problems 1—3

    |
M   |                         1   1         
a 14|                       1 1 1   4     1
c   |               1 1 1   2   1     1 2
h 12|           1 1 2   1   1   2   3 1
i   |                 1 1   1       1 1   
n 10|           1 1         1
e   |       2 1       1 1 4   1 1   1   1
   8|         2 1 1     1 
p   |         4 1 3 1   1
r  6|           2 2       
o   |             1
b  4|                         
l   |         1                
e  2|                          
m   |                                         
s _0|___________________________________________
    |0 . 2 . 4 . 6 . 8 . 10. 12. 14. 16. 18. 20
                        Exams

Summary: Exam scores above 10 (the median) were only achieved by those who earned at least 8 points on the machine problems, and those who earned 14 points on the machine problems all scored above 10 on the exams.

Homework 1—9

                                                X
Mean   = 18.20                      X X   X   X X
Median = 18.8                       X X   X X X X
                                    X X X X X X X X
                                    X X X X X X X X
                      X       X X X X X X X X X X X
                      X   X   X X X X X X X X X X X
 _____________X___X___X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X___X___
   0 . 2 . 4 . 6 . 8 . 10. 12. 14. 16. 18. 20. 22. 24. 26. 

Relationship between Exams 1—2 and Homework 1—9

    |
  28|
    |                         1
  24|                         1
H   |                   1   3   1 1 5 2 1 1
o 20|             2     2 1 2   1   2 1 2
m   |       1   1 2     1   1 1 2   2
e 16|       1 3 1 1 2 2 2 2
w   |         1 2 1 1     1
o 12|         1   1 1 1
r   |         1 1 1             1
k  8|         1   1             
    |           1
   4|         1
 __0|___________________________________________
    |0 . 2 . 4 . 6 . 8 . 10. 12. 14. 16. 18. 20
                        Exams

Summary: Exam scores above 10 (the median) were generally achieved by those with homework scores above 18 out of 27. Those with homework scores of 22 or greater rarely scored below 10 on the exams.

Total Scores

                                                      X
                                                  X   X
                                X X   X           X   X
                                X X   X           X   X X
Mean   = 38.92                  X X   X X   X X   X X X X
Median = 39.6             X   X X X X X X X X X   X X X X
                        X X   X X X X X X X X X X X X X X
 _______________X_______X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_______
   0 . 4 . 8 . 12. 16. 20. 24. 28. 32. 36. 40. 44. 48. 52. 56. 60
Rough grades: F         D         C         B         A     

Exam Solutions and Commentary

  1. Background: Consider this declaration from the code for class ScanSupport distributed on Oct. 25 and used in the posted solution to MP4.
    private static final Pattern intPattern = Pattern.compile( "-?[0-9][0-9]*|");
    

    a) Why use sc.skip(intPattern) instead of sc.skip("-?[0-9][0-9]*|")? (0.5 points)

    ___skip with a compiled pattern is faster than skip with a string______
    
    _______________________________________________________________________
    

    1/4 got this, almost 2/3 earned no credit, a few earned partial credit for saying vague things about efficiency.

    A common wrong answer included the assertion that skip(".*") skips the literal string ".*" but does not match the pattern. In fact, using it with a string parameter merely recompiles that parameter each time it is called. It is the cost of compilation that we save by pre-compiling the pattern.

    Another common wrong answer was that skip() only works with a compiled pattern. This is simply false.

    Finally, some suggested that it is easier to type. This is odd, given that the private static final pattern has an awfully long declaration, making it harder to type.

    b) We wrote "-?[0-9][0-9]*|" instead of "-?[0-9][0-9]*". what does our pattern match that the alternative does not? (0.5 points)

    ___the empty string____________________________________________________
    
    _______________________________________________________________________
    

    1/2 got this and 1/6 earned no credit.

    The most common source of partial credit was to confuse the empty string with either null (no string at all) or with whitespace. The empty string is not null, although people sometimes refer to it as the null string, and "" is not the same as " ".

    b) What does the pattern element -? (equivalent to (-|)) mean? (0.5 points)

    ___An optional dash____________________________________________________
    
    _______________________________________________________________________
    

    2/5 got this, 1/5 earned no credit.

    Some earned partial credit saying that this allows negative numbers or it allows signed numbers. The problem here is that they've jumped ahead to how the string is interpreted and said nothing about the meaning of the pattern element. Nothing in the question mentioned or asked about the meaning of the set of strings matched by the pattern.

    Fewer earned partial credit by mentioning that this pattern meant that something was optional, without saying exactly what was optional.

  2. Consider this code:
    class A {
        interface B { void a(); }
        public static void m( B x ) {
    
            ___x.a()__________________________________________________(a)
        }
    }
    class B {
        private void n() { ... some code ... }
        ... {
            ___A.m( ()->n() );________________________________________(b)
        }
    

    a) In blank (a), give the body of m that evaluates the lambda expression passed to m. (1.0 points)

    b) In blank (b), give code to call m passing it the parameterless method n for m to call. (1.0 points)

    1/5 got art a, 1/6 got part b.

    These were hard. Wild and crazy notation was common here. Given that everyone knew that lambda expressions were going to be on the exam, and given the importance of lambda expressions in the machine problems, this is the one question that everyone should have been focusing on preparing for over the past month. The performance of the class on this problem is clear evidence of how difficult lambda notation is for most students.

  3. Background: Our initial version of class ScanSupport.nextFloat() returned Float.NaN when there was no next floating point value. In the following, assume that the declaration float x is in effect.

    a) Why is it useless to write if(x==Float.NaN)? (0.5 points)

    ___NaN never equals any other value, even itself.______________________
    
    _______________________________________________________________________
    
    _______________________________________________________________________
    

    2/5 got this, and 2/5 earned no credit.

    A few gave answers that might have been correct but were so vague or badly worded that they got partial credit. Others got partial credit for saying that comparison was always false but giving mechanisms that were problematic.

    No credit was given to those who assrted that x==Float.NaN was problematic because x could be null. It cannot! Variables of type float (and int) do not hold object handles and can never be null.

    No credit was given for those who held that NaN was not a value of type float because it is. It stands for "not a number" but it is a value for variables that can hold a floating point number.

    b) Why is x.isNaN() potentially much slower than Float.isNan(x)? (0.5 points)

    ___Autoboxing._________________________________________________________
    
    _______________________________________________________________________
    
    _______________________________________________________________________
    

    1/7 got this. 5/8 earned no credit, mostly by giving fairly long but wrong answers.

    A longer correct answer would be that x.isNaN() really means Float(x).isNan(); that is, first an object in class Float must be constructed to hold the float value of x, and then the isNaN method of that object can be evaluated. Creating an object is not trivial even if that object is as simple as a Float.

    Many people focused on the idea that calls to methods of objects are slower than calls to static methods. This depends on the computer architecture and is not true on some architectures.

  4. Background: Sometimes, we can simply write aMethod(time) instead of:
    Simulator.schedule( time, (float t)->aMethod( t ) )
    

    Consider the following bits of code, where lines w, x, y and z are candidates for rewriting as suggested above. Assume that the variable this.i is a private field of this class.

    Simulator.schedule( time + delay, (float t)->aMethod( t ) );    //w
    Simulator.schedule( time, (float t)->aMethod( t, u, v ) );      //x
    Simulator.schedule( time, (float t)->ThisClass.aMethod( t ) );  //y
    Simulator.schedule( time, (float t)->OtherClass.aMethod( t ) ); //z
    this.i = this.i + 1;
    

    a) Which (if any) of wxyz could lead to events being simulated out-of-order if rewritten? (0.5 points)

    ___w___________________________________________________________________
    

    2/5 got this. Very few got partial credit. The most common wrong answer was x, given by 1/7.

    Rewriting line w as aMethod(time+delay) does computation that should be done at a future time now instead of waiting for that future time. All of the others involved computations that should take place now because they had no delays. Therefore, the issue with the others is conflicts in variable access, not simulation of events in the wrong order.

    b) Which (if any) of wxyz should not be rewritten if some statement in the body of aMethod contains the text this.i. (0.5 points)
    ___xy__________________________________________________________________
    

    Just 1 got this right, but 1/2 got partial credit. 1/5 said just y, 1/14 said just x, 1/7 said wx or yz.

    Cases x and y are equivalent because aMethod without qualification, is probably either this.aMethod or ThisClass.aMethod (which might be static). If it isn't static, a mention of this.i in aMethod will conflict with the assignment after line z, with the result that the value of this.i will be wrong if the code is executed in place instead of being scheduled.

    Because line z involves a method of some other class, a reference to this.i in that method is not a reference to the same i as is referenced in the given code.

    c) Which (if any) of wxyz could lead the compiler to complain that some variable is not final or effectively final? (0.5 points)

    ___x___________________________________________________________________
    

    4/7 got this right. Very few got partial credit. The most common wrong answer was w, given by 1/10; aside from this, it is hard to find a pattern in the wrong answers.

    Java's restriction that variables in lambda expressions must be final or effectively final only applies to variables that the lambda expression gets from its environment, not to variables passed as parameters. Only line x has any such variables, u and v.

  5. Background: Suppose m is a public method of class A and n is a private method of class B. How is it possible that we can arrange it so that m can call n. Assume neither class is a subclass or inner class of the other. (0.5 points)
    ___See problem 2 for code that does exactly this.______________________
    
    _______________________________________________________________________
    
    _______________________________________________________________________
    
    _______________________________________________________________________
    

    1/5 got this. Almost 1/2 earned no credit.

    An alternative answer: Code in B can pass a call to n to A.m in a lambda expression so that when A.m uses that parameter, it calls B.n.

    1/4 of the class earned almost full credit by suggesting adding a public method to B that can call n and then calling that method from within A.m.

    A small amount of credit was given to those who mentioned lambda or used lambda notation, no matter how broken the notation.

    A significant fraction of those who earned no credit proposed answers that violated the required assumptions by making one class a subclass or inner class of the other. In fact, making A a subclass of B does not give any code in B access to private fields of A; it only gives access to protected fields.

  6. Background: Here is the code for the simulator class we have been using, stripped of comments:
    class Simulator {
        public interface Action {
            void trigger( float time );
        }
    
        private static class Event {
            public float time;
            public Action act;
        }
    
        private static PriorityQueue  eventSet
            = new PriorityQueue  (
                (Event e1, Event e2) -> Float.compare( e1.time, e2.time )
            );
    
        public static void schedule( float time, Action act ) {
            Event e = new Event();
            e.time = time;
            e.act = act;
            eventSet.add( e );
        }
    
        public static void run() {
            while (!eventSet.isEmpty()) {
                eventSet.remove().act.trigger( e.time );
            }
        }
    }
    

    a) Underline the names of variables, fields and parameters in the above that can be legally declared to be final without making any other changes to the code. (0.5 points)

    1/3 were penalized for making the fields of class Event final. 5/8 did not note that eventSet could be final. 1/2 did not note that the parameters to schedule. 1/2 did not note that the local variable e in schedule.

    Some marked the parameter to the Action.trigger; given that it is unclear what final means in the parameter list to an abstract method, this was ignored.

    Some marked the lambda parameters in the call to the PriorityQueue constructor. Again, this is sufficiently obscure that these were ignored.

    Numerous students underlined entire assignment statements. These underlines were usually ignored because, from such an underlining, it is usually impossible to determine what variable, field or parameter is intended. Others underlined things that were not variables, fields or parameters, earning no credit for that effort.

    b) Suppose we declare the fields of class Event as final. Give the new code that must be added to class Event to allow this. (1.5 points)

    ___Event (float t, Action a) { // a new constructor____________________
    
    _______time = t;_______________________________________________________
    
    _______act = a;________________________________________________________
    
    ___}___________________________________________________________________
    

    1/5 earned full credit, 1/2 earned no credit (1/8 simply left it blank).

    Partial credit mostly went to those who gave something that could be repaired by deleting extraneous material. Several students gave essentially the above code, but added qualifiers such as class, static or private, creating confustion about whether this was intended to be a constructor or a method.

    Numerous students simply copied the code for class Event from above, adding the keyword final for the two fields. In effect, they merely stated, unnecessarily, the context created by the question and gave nothing that could be interpreted as an answer.

    Finally, numerous students spilled random lambda notation into the space available. This was utterly irrelevant.

    c) Rewrite schedule to use the code you added to class Event: (1.5 points)
    public static void schedule( float time, Action act ) {
    
        ___eventSet.add( new Event( time, act ) );_________________________
    
        ___________________________________________________________________
    }
    

    1/4 earned full credit, 2/5 earned no credit (1/8 left it blank).

    The fact that more earned full credit here than on part b) is a solid indicator that many who did not give good answers to part a) intended to write constructors regardless of the flaws in their notation.

    A surprising number of students gave code here that did not add an event to the event set or did not call a constructor to build a new event.

    As with part a), many students gave bizarre (and almost always syntactically invalid) lambda notation here. These earned no credit.