Assignment 13Due Apr 28, on line
Part of
the homework for CS:2820, Spring 2021
|
for (int i = 0; i < 30; i++) { final Lazy<Integer> b = new Memo<Integer>(a); a = ()-> b.eval() + b.eval(); }
This could be rewritten as follows:
for (int i = 0; i < 30; i++) { final Lazy<Integer> b = a; a = new Memo<Integer>( ()-> b.eval() + b.eval() ); }
Both versions work. Both versions make the loop run very fast compared to
the version without memoizatioin.
a) These are equally efficient.
b) The original is slower, it does an extra eval().
c) The original does an extra eval() plus it keeps a useless memo.
d) The new one is slower, it does an extra eval().
e) The new one does an extra eval() plus it keeps a useless memo.
— correct.
It really helps to look at problem 2) first! Looking at that answer, you see that the new one does not create a memo for the final item in the list, an instance of InitA, where it calls eval on that item twice, and it creates a useless memo for the head of the list, used only once, and therefore wasting effort, when eval is called on the entire list. This corresponds to answer e). The difference in performance is miniscule but real.
How is the list structured in the original version of the code?
The suggested answers below use the symbol => to mean
"which contains a handle for the an object in the class."
a) UpdateA => Memo => ... UpdateA => Memo => InitA
— correct.
b) Memo => UpdateA => ... Memo => UpdateA => InitA
c) UpdateA => Memo => ... Memo => UpdateA => InitA
d) Memo => UpdateA => ... UpdateA => Memo => InitA
e) none of the above.
In both versions of the code, each iteration creates one Update object and one Memo object in the same order. Therefore, c) and d) must be wrong because these imply either that the order switched halfway down the list or that the numbers of objects in each category are different.
Each iteration of the original version of the code creates a Memo first, then an UpdateA, so the memo end up wrapping the InitA object and the head of the list, at the end of any iteration, will be an UpdateA object. Therefore, a) is correct and b) is the structure created by the new version, not the original.
()-> b.eval() + b.eval()
Which of the following best shows what is actually being done here
a) ()->
Integer.decode( b.eval().toString() + b.eval().toString() )
b) ()->
new Integer( b.eval().toString() + b.eval().toString() )
c) ()->
new Integer( b.eval().intValue() + b.eval().intValue() )
— correct.
d) ()->
Integer.getInteger( b.eval().toString() + b.eval().toString() )
e) ()->
Integer.decode( b.eval().intValue() + b.eval().intValue() )
We are doing integer arithmetic, so nothing that involves toString can possibly be correct. That rules out a), b) and d).
Integer.decode(s) and new Integer(s) do the same thing when given a string s, so e) must be wrong because the parameter expression looks like it is trying to add two integers, not concatenate two strings.
The Integer.getInteger() method does something totally irrelevant, looking for a "system property." Therefore d) is nonsense.
That leaves c), which is, in fact, correct.
Note that "it breaks" only means we fail to remove circular dependencies. A circular dependency between top-level (outer) classes cannot be formed by private methods, so e) must be wrong. Similarly, all circular dependencies involve methods that are either public or default (package local). This means that d) proposes that no circular dependencies can be eliminated, which is also false.
Non-static methods are regular ordinary methods, the kind that permit removal of circular dependencies by having their code in classes that implement the interfaces they use. Therefore, c) is exactly wrong.
Constructors cannot be part of an interface because you can only construct instances of a particular class. Therefore if the constructors of the two classes each have occasional need to call the constructor of the other class, the strategy outlined will not work. This makes b) the correct answer.
How many calls to p.infect() for various people p are scheduled as a result of the above assuming the code from the distributed solution to MP11 is used?
Make has a special case to detect and deal with circular dependencies like
the above. What would happen here if it did not have this special case?
a) 9
b) 10
c) 18
— correct.
d) 20
e) some other number.
When the infected person arrives at the place, calls to infect() will be made for every person already in that place, scheduling an infection event at a finite future time for everyone already there. When the remainder of the people arrive, each one will check with the place, find that there is someone there who is infected, and schedule an infection event at a finite future time. The only one who does not schedule such an event is the already infected person. So a total of 9 events are scheduled.
On departure from the place, similar logic is followed, except that the new infection events are scheduled at an infinite future time. So again, 9 events are scheduled, bringing the total to 18, 9 at finite future times, 9 at infinite future times.