Exam 3: Final

Solutions and Commentary

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

Grade Distributions

The course policies posted at the start of the semester stated that:

Collegiate norms suggest that in typical offerings at this level, about 60% of those who actually take the course (that is, who attend, do the assignments and take the exams) will earn at least a B.

In keeping with this policy, students who did not take the final are not included in the following grade distributions, and students who were noted not to have attended class on 8 or more occasions when attendance was noted are marked with O in the following histograms and are not included in the grading statistics.

Most notes on attendance were made by noting who did not collect homework when it was distributed in discussion sections or in lectures.

Final exam

Mean   = 8.03
Median = 7.7
                X             O       X
                X   X X       X X X   X             O     X
        X   X X X O X X   X O X X X X X             X O   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. 11. 12. 13. 14. 15. 16. 17. 18

Total exams

                                      X
Mean   = 17.58                        X
Median = 18.3                         X
                                    X X
              O X   X       X X     X X         X           X
            X X X X X X O   X X   O X X O X O   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____
 . 2 . 4 . 6 . 8 . 10. 12. 14. 16. 18. 20. 22. 24. 26. 28. 30. 32. 34. 36. 

Total machine problems

                                                      X
                                                      X
                                                    O X
                                                    X X
                                                    X X
                                        X     X   O X X
Mean   = 22.48                    O     X     X   X X X  
Median = 24.6                     X X   X O   X   X X X X X
                            X   O 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. 28. 30

Total of top 10 scores from homework assignments 1 — 12

                                                  X      
                                                  X      
                                                  X      
                                                  X      
                                                  X      
                                              O O X     X
                                              X X X   X X
                                              X X X   X X
Mean   = 24.68                                X X X O X X
Median = 24.6                               O X X X X X X X X
                                        O 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. 28. 30

Total scores

                                  O           X
Mean   = 64.99            X     X O           X X
Median = 66.?             X O X X O X     X X X X O               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_____
 24. 28. 32. 36. 40. 44. 48. 52. 56. 60. 64. 68. 72. 76. 80. 84. 88. 92
           |- -   D   + +|- -   C   + +|- -   B   + +|- -   A   + +|

Relation between work load and success

--- \                             -           6
68-  \W   4 )F            8   - - -           5 5
555  /                    5 6 5 6 - 5     7 5 5 4 5         7     6
344 /             - 5     5 6 5 5 6 5     6 4 4 3 5     5 5 6 7   5
________________4_4_4_6___4_5_5_4_5_4___3_4_4_3_2_4_6_4_4_3_4_4___5_____
 24. 28. 32. 36. 40. 44. 48. 52. 56. 60. 64. 68. 72. 76. 80. 84. 88. 92
           |- -   D   + +|- -   C   + +|- -   B   + +|- -   A   + +|

The data from the first-day-of-class survey included the student's work load, in hours per week. Combining this data with the grade distribution reveals some interesting things. In the above, single digits indicate the student's self-reported work hours per week, divided by 10, so 4 indicates a load of from 40 to 50 hours per week. Students who missed the first day of class are indicated with -. Note that this data includes students who were not included in the histograms above because they missed the final or missed too many classes.

No student who missed the first day of class earned a grade higher than C, and almost half of those who missed the first day of class later dropped the course. The one student who earned an F by doing no work all semester reported a load of only 40-50 hours. Of the two students reporting loads over 80 hours per week, one dropped the course and one earned a C-, but the three students reporting loads over 70 hours per week earned B+ or better.

Solutions and Commentary

  1. Background: Consider Java code in Appendix A.

    a) You can infer a complete definition for A from the code in Appendix A. Give it. (1.0 points)

    ____interface A {______________________________________________________
    
    ________int a( int b );________________________________________________
    
    ____}__________________________________________________________________
    
    _______________________________________________________________________
    

    1/3 did well, slightly more earned no credit.

    Of those earning partial credit, common errors included declaraing A to be a class, not an interface, or adding a body of some kind. Less common errors included unnecessarily declaring a to be public or of type void, forgetting the parameter list, or adding a constructor for A.

    b) If class B is declared as an inner class right before it is used in the for loop in Appendix A, the declaration can be shorter. Give the shortest possible form in this context. (1.5 points)

    ____class B implements A {_____________________________________________
    
    ________public int a( int b ) {________________________________________
    
    ____________return 2 * b + j;__________________________________________
    
    ________}______________________________________________________________
    
    ____}__________________________________________________________________
    
    ____A a = new B();_____________________________________________________
    

    Only 1/12 did well, while 1/4 earned no credit.

    A small but common error was to eliminate final gratuitously. The most important common errors were to unnecessarily retain the explicit constructor, since an inner class can get j directly from the surrounding context, or worse yet, retain the constructor and break it.

    c) Rewrite new B(j) from Appendix A so that it uses an anonymous implementation of A instead of the inner class B. (1.5 points)

    ____new A() {__________________________________________________________
    
    ________public int a( int b ) {________________________________________
    
    ____________return 2 * b + j;__________________________________________
    
    ________}______________________________________________________________
    
    ____}__________________________________________________________________
    

    1/10 did well, over 1/2 earned no credit.

    Among those earning partial credit, Several retained vestiges of a constructor, many made syntax errors, and several used new B despite the fact that this code eliminates B as a named class.

    d) Give a λ expression that is equivalent to new B(j) in Appendix A. (1.0 points)

    ____() -> 2 * b + j____________________________________________________
    
    _______________________________________________________________________
    

    1/5 did well, 2/5 earned no credit

    The most serious common error was to mess up access to j, for example, by naming the λ parameter j. Other common errors included adding an explicit constructor call such as new A, giving a parameter, or worse, giving one with no type, giving a body to the λ expression more complex than a simple expression, or a variety of syntax errors.

    The entire problem 1 of the exam was basically a rerun of problem 1 on exam II, or problem 1 on homework 10, but working from an outer class to a lambda expression instead of working from the lambda expression to a class definition. Everyone was well warned by those problems that they should be prepared for something like this.

  2. Background: Consider Java code in Appendix A.

    a) Class B in Appendix A is final. What does declaring it final prevent? (0.5 points)

    ____it prevents creating subclasses of B_______________________________
    

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

    Partial credit was given for garbled mentions of class hierarchy. A common answer was that "it prevents creation of class instances." This is simply not true. Another common answer was that "it prevents changing fields of the class," also not true.

    b) The declaration int i in Appendix A could have some combination of public, private, restricted or final in its declaration. Which (if any) of these would be appropriate to use here? (0.5 points)

    ____private final______________________________________________________
    

    1/4 did well, 1/6 earned no credit.

    1/7 forgot private, 1/3 forgot final. Several suggested public, which, although it would not break the program, would not be appropriate because there is no need to ever reference i from outside an instance of B. Similarly, some suggested protected, but with no subclasses of B there's no possible reason that this would be appropriate.

  3. Background: Consider the code in Appendix B, an abridged version of class Simulation from HW13 (comments and inner class Semaphore have been deleted). Also, consider the code in Appendix C, lifted from class Source in the last version of the road network simulator distributed to the class, but with comments deleted.

    A problem: Rewrite Source.exit to work under the new class Simulation. (1.5 points)

    ____private void exit() {______________________________________________
    
    ________this.pickRoad().enter();_______________________________________
    
    ________number = number - 1;___________________________________________
    
    ________if (number > 0) {______________________________________________
    
    ____________Simulation.schedule( interval, ()->this.exit() );__________
    
    ________}______________________________________________________________
    
    ____}__________________________________________________________________
    

    1/10 did well, over 1/2 earned no credit (fully 1/6 simply copied the code from Appendix C, either making no changes or copying with errors).

    Common errors included passing the time as parameter to Source.exit, or passing the time to Road.enter(). This suggests a failure to understand that the new framework always has the time available as Simulation.time, so the time is never passed as a parameter to any event-service routines.

    Other categories of errors included failing to correctly formulate the λ expression, failing to use a λ expression (or an equivalent constructor), or failing to pass the delay properly to Simulation.schedule.

    Problems here reflect a failure to take full advantage of the study questions distributed a week before the exam in the form of Homework 13.

  4. Background: Consider this quote from class ScanSupport:
    private static final Pattern theRest =
            Pattern.compile( "[^\n]*" );
    

    a) What does this pattern match? (0.5 points)

    ____Zero or more repeats of any character other than newline___________
    
    _______________________________________________________________________
    

    1/4 got this. Slightly more earned no credit. The most common wrong answer was to say that this pattern matched blanks or white space.

    Partial credit was given to those who mentioned newlines as something important to this pattern, or who said it matched everything but something.

    b) Why did we write sc.skip(theRest) instead of sc.skip("[^\n]*")? (1.0 points)

    ____To avoid the implicit cost of a pattern compile on each skip_______
    
    _______________________________________________________________________
    
    _______________________________________________________________________
    

    1/3 got full credit. Slightly more earned no credit. The most common answer that was worth nothing was that "sc.skip with a string parameter simply skips that string literal." This is not true.

    Vague mention of some kind of pattern compiler earned partial credit.

    c) After calling sc.skip(theRest), suppose we call sc.skip(theRest) again. What does the second call do? Why? (1.0 points)

    ____nothing____________________________________________________________
    
    ____because it won't advance beyond the newline________________________
    

    Almost 1/2 earned full credit. Over 1/3 earned no credit.

    Partial credit went to those who understood that it did nothing but for reasons that were either incompatible with their answers to part a) or that were simply wrong.

    Given the work we did with scanners and regular expressions, forgetting how they worked was not a good idea. Assignment 5 asked you to study the methods of class Scanner, Assignment 6, problem 2 required you to write some patterns. If you gained any facility with vi you are also likely to have learned more about patterns.

  5. Background: Developers are told to use new Random(1) while debugging, and then switch to new Random() once they get their code to work.

    A problem: Why? (0.5 points)

    ____Using the same seed for every run makes errors repeatable__________
    
    ____Not specifying the seed lets the system make things unpredictable__
    
    _______________________________________________________________________
    

    1/3 got this, slightly fewer earned no credit.

    Partial credit was given for answers such as "it gives a more predictable sequence."

    Like the question about scanners, pseudo-random-number generators weren't central to the course, but we used them enough that students should have remembered a few basic facts.

  6. Background: In our road network simulator, class RoadNetwork maintained the collection of all intersections, and RoadNetwork.findIntersection was used to look things up. In the logic simulator, class Gate maintained the gate collection, and Gate.findGate() was used to look things up.

    A problem: Why is the organization of the logic simulator better? Answers that do not fit in the space provided will be ignored. (1.0 points)

    ____It eliminates a cycle in the class dependency graph or_____________
    
    ____It splits a layer in the class hierarchy___________________________
    
    _______________________________________________________________________
    

    Only 1 earned full credit. 1/2 earned no credit. Fully 1/6 focused on the efficiency of the code, a non-issue.

    The most common answer earning partial credit involved aesthetics, suggesting that putting the code to search for members of a class inside that class is prettier than putting it in some other class. While this is true, the reasons that earned full credit offer an objective justification for the aesthetic answers.

  7. Background: Appendix D contains a makefile that was distributed with HW13, minus comments.

    a) Suppose you just typed the shell command make clean and now you type make tests. What shell commands does make run? (0.5 points)

    ____javac @classes_____________________________________________________
    
    ____sh tests___________________________________________________________
    

    1/7 got this, almost half earned no credit.

    The most common error was to omit javac @classes. Another common error was to add tests logicsim.class as if it was a shell command.

    b) Suppose, after part a, you just edited Wire.java and you type just make. What shell commands does make run? (0.5 points)

    ____javac @classes_____________________________________________________
    
    _______________________________________________________________________
    

    1/7 got this, half earned no credit. Among those who earned no credit, a significant number simply extracted all the shell commands from the makefile, as if typing make with no arguments meant to do everything, ending with the consequences of make clean, which deletes all the results of the previous steps.

    Comon errors included javac *.java, a command that does much the same thing, but is not the shell command in the make file, and as in part a), reading *.java classes as a shell command.

    c) Suppose you wanted the command make docs to make all the Javadoc documentation for your project. What new lines would you add to the makefile? (1.0 points)

    ____docs:______________________________________________________________
    
    ____________javadoc @classes___________________________________________
    

    1/4 got this, 1/6 earned no credit.

    Among those earning partial credit, many used *.java instead of @classes, a minor issue. A number left out the newline, putting the javadoc command on the same line as the docs header. The latter is consistent with those who thought that the additional content on the headlines in parts a) and b) was interpreted as shell commands.

    It was legal to write docs: *.java classes as the headline, documenting the files that the docs step depends on. Fully 1/12 of the class tried to write dependencies here that were defective.

    Students who ignored the study questions in homework 13, or rather, ignored the suggestions about how to make the software distributed with homework 13 were at a disadvantage here, and that should have lead students to paying a little attention to Lecture 35 where we discussed makefiles. Simply reading the makefile in Appendix D and recognizing the lines in it that were shell commands should also have helped. There was no need to try to write creative shell commands — make never does that.

  8. Background: The Java expression a+b+c is easy to evaluate if the variables are of type int. What if they are of class String?

    A problem: Give the long-winded Java equivalent of a+b+c for strings. Reminder: A new object of class stringBuilder is involved, and that class has an append method. The result must be a String. (1.0 points)

    ____StringBuilder( a ).append( b ).append( c ).toString()______________
    
    ____StringBuilder().append( a ).append( b ).append( c ).toString()_____
    

    Just 1/30 did well. 3/12 earned no credit.

    By far the most common error was to omit toString() at the end. StringBuilder objects are not strings. They must be made to be strings except when they are used in contexts where Java is willing to automatically tack on a toString() for you.

    Forgetting to use new to construct the new object was also a problem, or saying new StringBuilder without a parameter list for the constructor. Others creatively invented 2-argument versions of append. Several students added a return statement at the end or declared a temporary variable of class StringBuilder, as if this was the body of a method and not simply part of an expression; some compounded the error by using a temporary of class String.

    Note that this material was covered in the notes for Lecture 17 as motivation for introducing λ expressions. The notes for Lecture 11 also mentioned the high cost of concatenating strings, noting that it always involves constructing a new object.

  9. Background: Appendix E gives the code for Simulation.Semaphore from HW13, minus comments. Assume that the variable s is an instance of this class.

    a) Suppose some simulation event handling method ends with the call s.wait(1.0,()->something()). Under what circumstances is something() called scheduled immediately, that is, at the current simulated time? (1.0 points)

    ____count > 0__________________________________________________________
    
    _______________________________________________________________________
    

    1/2 got this, just under 1/2 earned no credit.

    The few who earned partial credit did so by adding incorrect additions to the correct answer.

    This is a problem where simply reading the code without understanding it led naturally to the correct answer. The most common wrong answer, variations on "an empty queue" is contradicted by the simple fact that there is no point in the code for wait that checks for an empty queue.

    b) Continuing part a, if something() is not called immediately, what will cause it to be called at some later time, and what will the simulated time be when it is called scheduled? (1.0 points)

    ____a call to signal at time t would schedule someting at t+delay______
    
    _______________________________________________________________________
    

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

    The most common error was to assume that this question was simply the inverse of part a). It was not. This problem assumed the inverse of part a), that is, that wait had been called with count ≤ 0, and then it asked about what happened next. As with part a, several added wrong material to a right answer for partial credit, or gave vague answers.

    Note that the typos in the exam (corrected with overstrike and underline above, but corrected on the whiteboard during the exam itself) made it difficult to grade part 2 of this question, so anything vaguely sensible was accepted.

    c) Why is class Semaphore an inner class of Simulation? (0.5 points)

    ____because its implementation needs access to private class Event_____
    
    _______________________________________________________________________
    

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

    Some partial credit was offered for convenience arguments. For example, that the methods in Semaphore can just call schedule instead of needing the more verbose Simulation.schedule. Others gave vague answers about needing to use unspecified private components of Simulation.

    The most shockingly wrong answers given were that it was local to Simulation because it was only needed inside that class. Appendix B contains all the remaining code of class Simulation, and nothing in that code uses Semaphore, so this is obviously wrong. The only example use of class Semaphore you were given was in Homework 13, and that example was definitely not part of class Simulation.

    d) Why must class Semaphore be declared to be static? (0.5 points)

    ____Because class Simulation has no instances it could depend on_______
    
    _______________________________________________________________________
    

    Only 1/15 got this, while 5/6 earned no credit.

    Vague answers earned partial credit.

    Shockingly wrong answers suggested a failure to understand static, for example, suggesting that it had to be static so that instances could be constructed outside of class simulation. This seems to confuse static with public.

    e) If we declare class Semaphore to be final, what does this do? (0.5 points)

    ____It prevents creation of classes that extend it_____________________
    
    _______________________________________________________________________
    

    1/6 got this, while 7/12 earned no credit.

    Partial credit was given to many who said that it prevents adding or changing methods. Technically, methods in a class can never be added or changed. Each subclass is a new class that inherits some methods of its parent and has some new methods of its own.

    Even worse, some students talked about changing fields of the class, as if it was an object whose values were being changed. This seems to confuse final classes with final variables. This earned no credit, but there were vague answers that might have been about variables and might have been about classes.

  10. Background: Consider the code for class Simulation in appendices B and E combined. Given this code, suppose someone wrote Simulation s=new Simulation().

    a) What is the object you get for s? Is it of any use? (0.5 points)

    ____It is an empty useless object of class Simulation__________________
    
    _______________________________________________________________________
    

    3/10 got this, 1/5 earned no credit.

    Partial credit was typically the result of failing to answer the "use" part of the question or inventing uses for the object.

    All of the uses people suggested were wrong. Most who suggested uses for instances of Simulation suggested that each instance could be used to run a different simulation. The trouble with this is that it is plainly false because all of the methods and fields of Simulation are static.

    Nobody mentioned the one plausible use for empty objects of any class: a variable of that class can be null or non-null, and an empty object has a non-null handle. Such variables can be used as alternatves to booleans, so they are not entirely useless.

    b) How could we make it illegal to create an instance of Simulation? (0.5 points)

    ____Add a private constructor__________________________________________
    
    ____Have the constructor throw an error________________________________
    

    Only 1/20 got this. 6/10 earned no credit.

    Many earned most of the credit for suggesting making the class abstract. The problem is, this allows subclasses, and each instance of a subclass includes an instance of its parent class as something of a prefix. Others earned partial credit for answers that were vague.

    Many wrong answers suggested making the class static or final. Neither of these has any effect on the ability to create class instances.

  11. A little problem: Simulation.schedule in Appendix B is missing a sanity check. What code would be a good idea to add to it? (1.0 points)
    ____if (delay < 0) throw new java.lang.Error( "negative delay" );______
    
    _______________________________________________________________________
    
    1/4 got this. 1/3 earned no credit. The most common wrong answers involved checking the wrong condition, for example, checking for an empty event set.

    This asked for code, but many earned almost full credit by giving long winded English answers, usually a bit vague about the details.
Afterword: The single biggest problem on the exam was failure to read the code in the appendices. If nothing else, this was a semester where students should have gotten pretty good at reading fairly large bodies of code.

Code Appendices

Carefully tear this page off of the staple so you can refer to it while working on the exam:

Appendix A

final class B implements A {
    int i;
    B(int i) {
        this.i = i;
    }
    public int a( int b ) {
        return 2 * b + i;
    }
}

public class C {
    public static void main( String[] args ) {
        for (int j = 1; j < 10; j++) {
            A a = new B( j );
            System.out.println( a.a( 1 ) );
        }
    }
}

Appendix B

public class Simulation {
    public static float time = 0.0f;

    public interface Action {
        void trigger();
    }

    private static class Event {
        final float time; // the simulated time of the event or delay in queue
        final Action act; // the action to trigger at that time

        Event( float t, Action a ) {
            time = t;
            act = a;
        }
    }

    private static final PriorityQueue <Event> eventSet
        = new PriorityQueue <Event> (
            ( Event e1, Event e2 ) -> Float.compare( e1.time, e2.time )
    );

    public static void schedule( float delay, Action a ) {
        eventSet.add( new Event( time + delay, a ) );
    }

    public static void run() {
        while (!eventSet.isEmpty()) {
            Event e = eventSet.remove();
            time = e.time;
            e.act.trigger();
        }
    }
}

Appendix C

private void exit( float now ) {
    this.pickRoad().enter( now );
    number = number - 1;
    if (number > 0) {
        Simulation.schedule( now + interval, (float t)->this.exit( t ) );
    }
}

Appendix D

# Makefile
Logicsim.class: *.java classes
        javac @classes
tests: tests Logicsim.class
        sh tests
clean:
        rm -f *.class

Appendix E

public static class Semaphore {
    private int count = 0;
    private final LinkedList <Event> queue
        = new LinkedList <Event> ();

    public Semaphore( int c ) {
        if (c < 0) throw new java.lang.Error(
            "Semaphore must not be created with a negative count."
        );
        count = c;
    }

    public void wait( float delay, Action a ) {
        if (count > 0) {
            count = count - 1;
            schedule( delay, a );
        } else {
            queue.add( new Event( delay, a ) );
        }
    }

    public void signal() {
        if (queue.isEmpty()) {
            count = count + 1;
        } else {
            Event e = queue.remove();
            schedule( e.time, e.act );
        }
    }
}