The minimum amount of code you needed to write was:
public class Counter {Though many of you recognized this wasn't particularly useful (how do you find out the value of your counter?) and write something closer to:
int i = 0;
public void increment() {
i++;
}
}
public class Counter {In either case the initial value of the counter comes from the line int i = 0; This is a statically determined value. To dynamically determine the value, you could have written the following:
private int i = 0; // Note the use of the keyword private
public void increment() {
i++;
}
public int getValue() {
return i;
}
}
public class Counter {
int i;
// Using a constructor...
public Counter(int startInt) {
i = startInt;
}
public void increment() {
i++;
}
}
package BinSort;
public BoringNodeBehavior implements NodeBehavior {
public void act(InputChannel[] inputChannels,
OutputChannel[] outputChannels) {
// act is empty.
}
}
package Binsort;Alas, we find that while we seem to do okay in certain circumstances, our code can crash, eat packets, or create them. How can we fix this? Well, the first problem, our code can crash, is caused by the lines where we access the 1st element of the array (either the input or the output) whithout ensuring that that channel even exists! Imagine the situation where there are no inputs or outputs. Does it make sense to access the first (non-existant) element? We remedy the situation with the following modifications:
public Strategy1_1 implements NodeBehavior {Object o; // storage for the object we will read/write
public void act(InputChannel[] inputChannels,
OutputChannel[] outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
o = inputChannel[0].readObject();
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
outputChannels[0].writeObject(o);
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package Binsort;Now we need to take care of the problems where we create and destroy packets. How do we create packets? Well, suppose we have read a packet and successfully written it. Our act method is called again and this time we fail to successfully read a packet, but Object o is still successfully written to the output channel. Oops! We need to prevent this double (or triple, or quadruple, or...) writing somehow. The following does the trick. Now whenever we write a packet we prevent ourselves from ever writing it again by unsticking our label and checking to make sure we only write packets if Object o is non-null.
public Strategy1_2 implements NodeBehavior {Object o; // storage for the object we will read/write
public void act(InputChannel[] inputChannels,
OutputChannel[] outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input
if (inputChannels.length != 0) {
o = inputChannel[0].readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output
if (outputChannels.length != 0) {
outputChannels[0].writeObject(o);
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package Binsort;The final problem we face is that our node sometimes eats packets, meaning that not every packet that enters via one of the inputChannels makes it to an OutputChannel. Borrowing from the above code, though, we can see that the problem is that we read a packet, regardless of whether or not we wrote the one we already had. To prevent this, only do a readObject if o is null (has been written). This final implementation fo our first strategy is thus:
public Strategy1_3 implements NodeBehavior {Object o; // storage for the object we will read/write
public void act(InputChannel[] inputChannels,
OutputChannel[] outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input
if (inputChannels.length != 0) {
o = inputChannel[0].readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output, and check to make sure
// we have a packet ready to write.
if ((outputChannels.length != 0) && (o != null)) {
outputChannels[0].writeObject(o);
// if no exception is thrown, we have successfully written
// the packet, so unstick the label from the Object.
o = null;
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
package Binsort;
public Strategy1_4 implements NodeBehavior {Object o; // storage for the object we will read/write
public void act(InputChannel[] inputChannels,
OutputChannel[] outputChannels) {
// first we try to read a packet, catching the
// necesssary exceptions
try {
// check for at least one input, and make sure we're not
// already holding an Object.
if ((inputChannels.length != 0) && (o == null)) {
o = inputChannel[0].readObject();
}
} catch (ChannelEmptyException e1) {
} catch (ChannelDisabledException e2) {}// now we have a packet, so we try to write it.
try {
// check for at least one output, and check to make sure
// we have a packet ready to write.
if ((outputChannels.length != 0) && (o != null)) {
outputChannels[0].writeObject(o);
// if no exception is thrown, we have successfully written
// the packet, so unstick the label from the Object.
o = null;
}
} catch (ChannelFullException e3) {
} catch (ChannelDisabledException e4) {
}
}
// counters to determine which input/output
to write to first
private int nextInIndex, nextOutIndex;
// the packet we read/write
private Object packetToBeSent;
private void readFromChannel(InputChannel[] inputChannels)
{
// if there's no input channel, exit right
away. Note that this
// is not strictly necessary (the for loop
is correct even for
// the case where we have length ==
0.)
if (inputChannels.length == 0) return;
// try to read from each InputChannel.
Stop when either:
// 1. a packet has been successfully read,
or
// 2. when we have tried each input channel
at least once.
for (int i = 0; i < inputChannels.length &&
!readToWrite; i++)
{
try
{
packetToBeSent =
inputChannels[ nextInIndex ].readObject();
readyToWrite = true;
}
catch(ChannelEmptyException exc) {}
catch(ChannelDisabledException exc2) {}
// Increment the channel we will
read from, modulo length
nextInIndex = (nextInIndex + 1) % inputChannels.length;
}
}
private void writeToChannel(OutputChannel[] outputChannels)
{
// If there's no out channel, exit right
away. As above, this
// is not stricly necessary.
if (outputChannels.length == 0) return;
// try to write to each OutputChannel.
Stop when either:
// 1. a packet has been successfully
written, or
// 2. when we have tried each out channel
at least once.
for (int i = 0; i < outputChannels.length &&
readToWrite; i++)
{
try
{
outputChannels[ nextOutIndex ].writeObject(
packetToBeSent );
readyToWrite = false;
}
catch(ChannelFullException exc) {}
catch(ChannelDisabledException exc2) {}
// Increment the channel we will
write to, modulo length;
nextOutIndex = ++nextOutIndex % outputChannels.length;
}
}
public void act(InputChannel[] inputs,
OutputChannel[] outputs)
{
if (readToWrite) { writeToChannel( outputs ); }
else /* not readToWrite */ {
readFromChannel( inputs ); }
}
}
Important Note:
IntermediateNodeBehavior is not necessarily the best, most efficient, or most "correct" implementation of NodeBehavior. Many of you have code that is as effective in moving packets. Your code is correct too. The code we have given you here is one possible solution, and probably could be improved upon. Indeed, some of the things included in our code are unnessary for correctness, but were included to make what was happening mroe clear. For instance, we do not really need to have the lines:
if (inputChannels.length == 0) return;becuase the for loop that follows would correctly exit without ever attempting a read/writeObject. So, if you think your implementation is better than ours, let us know and we'll post it to the homepage.
if (outputChannels.length == 0) return;
This course is a part of Lynn Andrea Stein's Rethinking CS101 project at
the MIT AI Lab and the Department of Electrical Engineering and
Computer Science at the Massachusetts
Institute of Technology.