Assignment 8, due Mar 24

Solutions

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

On every assignment, write your name and section number. Write your name as it appears on your university ID card! Use the section number as it appears in your current registration! We will not do detective work to figure out who did what. Homework is due on paper at the start of your discussion section. Exceptions will be made only by advance arrangement with your TA (excepting "acts of God"). Never push homework under someone's door!

  1. Background: In the simulation framework (class Simulation) in the code distributed on March 10, there is an inner class Event that is declared to be static. This code compiles correctly, but if the attribute static is deleted from the declaration of Event, you get an error.

    a) The error message says that some variable cannot be accessed when calling the constructor new Event(). What variable? (0.4 points)

    The variable is this in Simulation.schedule().

    Here is the error message you get; note that because the word this is not in quotes, it is easy not to read it as a variable name. This problem with readability leads the reader to ask "this what?"

    RoadNetwork.java:139: error: non-static variable this cannot be referenced from a static context
                    eventSet.add( new Event( time, act ) );
                                  ^
    1 error
    

    To explain this error in more detail, the constructor Event(time,act) has two explicit parameters, and if class Event is not static, it also has an implicit parameter, the object of class Simulation within which a new Event is being constructed. If Simulation.schedule() was not static, then this would refer to an object of class Simulation. Because Simulation.schedule() is static, this is undefined so we get the error message shown above.

    b) The variable in question is passed as an implicit parameter to the constructor. In this case, the parameter is never needed. There are three reasons for this, any one of which is sufficient. Give them. (Hint: All of them are negative reasons -- that is, thing that are not present or that the program never does and each can be expressed in a short sentence.) (0.6 points -- 0.2 for each reason)

    First, no instances of class Simulation are ever created.

    Second, class Simulation does not contain any non-static fields or methods, so even if an instance of Simulation was created, there is no way that any code in class Event could atempt to reference them.

    Third, even if an object of class Simulation was created and even if it contained non-static fields or methods, none of the code within class Event refer to any fields or methods of class Simulation.

    In effect, source code examination allows us to talk about an inner class being effectively static despite not being tagged with the static keyword. The Java compiler doesn't notice this, although it does have a concept of a variable being effectively final.

  2. Background: The posted solution to Machine Problem 3 does not contain any sanity checking code. The obvious sanity check is to compare the number of wires going into a gate with the declared count of inputs. Obviously, we can add code analogous to the RoadNetwork.checkNewtork() code distributed on March 10. This would call g.check() for every gate g. We also need each wire to call addIncoming() for the gate to which it connects.

    a) Write the code for the addIncoming() method in class Gate, along with any new fields of class Gate that this requires. Do not write comments, but please indent your answer properly. (0.5 points)

    Here is a simple solution:

    private int incount = 0; // wires as they are connected to this gate
    public addIncoming() {
            incount = incount + 1;
    }
    

    And here is a more complicated but perhaps more obvious solution: Here is a simple solution:

    private final LinkedList  incoming = new LinkedList  ();
    public addIncoming( Wire w ) {
            incoming.add( w );
    }
    

    The second solution may be overkill, because for sanity checking, the only thing we do with the list incoming is check its size.

    b) Write the code for the check() method in class Gate that works with your code above plus the existing code of the class. Do not write comments, but please indent your answer properly. (0.5 points)

    Here is the simple solution; the solution that works with the more complicated solution above is obvious enough that it isn't given here:

    public void check() {
            if (incount != inputs) {
                    Errors.warn(
                            this.toString() + " improper number of inputs"
                    )
            }
    }
    

    It might be better to divide the warning into one saying "too many inputs" and one saying "too few inputs", but for the purpose of this question, this suffices.

    Why no comments above? Because these answers are so short that comments will just slow us down when we read them. In the context of a whole program, they probably do need, at the bare minimum, header comments.

  3. Background: Consider the problem of writing a ternary logic simulator using the simulation framework distributed on March 10. The ternary values are 0-false, 1-unknown, and 2-true. Each wire and gate object has two new methods, inputChange(t,o,n) and outputChange(t,o,n). The parameters are: usually be deleted without causing any problems. Note the word usually. There are exceptions that occur when there is a class hierarchy and the parent class contains a private final method.

    Assume that class Gate is augmented with the following new fields:

    a) Write the code for Wire.inputChange(). This is fairly simple, since all it does is schedule a corresponding output change after the wire's delay. (0.5 points)

    public void inputChange( float time, int old, int new ) {
            Simulation.schedule(
                    time + delay,
                    (float t) -> outputChange( t, old, new )
            );
    }
    

    b) Write the code for MinGate.inputChange(). This is harder because it has to determine whether the output should change. Suggestion: Update inputCounts after each input by decrementing the array entry for the old value and incrementing the entry for the new value. Then compute the new output value as the minimum array index for which the array entry is nonzero. Finally, if the new output value differs from output, schedule an output change and update the value of output. (0.5 points)

    public void inputChange( float time, int old, int new ) {
            inputCounts[old]--;
            inputCounts[new]++;
            int newOutput = 0;
            while (inputCounts[newOutput] == 0) newOutput++;
            if (output != newOutput) {
                    final int old = output;
                    final int new = newOutput;
                    Simulation.schedule
                            time + delay,
                            (float t) -> outputChange( t, old, new )
                    );
                    output = newOutput;
            }
    }
    

    The one tricky thing in this code is the pair of final variables in the inner block. You'd think you could write this lambda expression without needing those two variables:

                            (float t) -> outputChange( t, output, newOutput )
    

    The problem is, Java demands that the variables referenced from within a lambda expression be "final or effectively final". In the case of output, it is obviously not final because the immediate next thing done in the code is to assign a new value to output. When the lambda expression's body is eventually evaluated, we want it to use the old value of output and not the value that is about to be changed. Thus, we really do need a copy of this value. In the case of newOutput is is, in fact, effectively final because no further changes in its value will take place after the while loop terminates. The Java compiler, however, may not be smart enough to figure this out.