Exam 3: FinalSolutions and Commentary
Part of
the homework for CS:2820, Spring 2019
|
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.
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
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.
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
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
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 + +|
--- \ - 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
____if (delay < 0) throw new java.lang.Error( "negative delay" );______ _______________________________________________________________________
Carefully tear this page off of the staple so you can refer to it while working on the exam:
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 ) ); } } }
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(); } } }
private void exit( float now ) { this.pickRoad().enter( now ); number = number - 1; if (number > 0) { Simulation.schedule( now + interval, (float t)->this.exit( t ) ); } }
# Makefile Logicsim.class: *.java classes javac @classes tests: tests Logicsim.class sh tests clean: rm -f *.class
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 ); } } }