Introduction to Programming: Comment on Test 3

Although I did go through this test in class, it was on the day of the tube strike, so many people had genuine difficulties getting in. So I'm giving some written observations on the test here.

The test was marked out of 80, and the 20 marks for each question were divided equally among its parts (with 7 marks for each part of questions 2 and 3, but a maximum of 20 overall). However, since the test was done very badly by most people, and there were complaints that it was too much for the time available, I have decided to grade it by counting complete and correct answers to three questions as enough to get full marks. In other words, a mark of 60 is equivalent to 100%, and the pass mark is 40% of 60, which is 24. The three people who got over 60 marks will be recorded as getting 100%.

Even after this scaling, over half those taking the test failed. I do not believe this is an acceptable performance. Anyone who had paid any attention to what was going on in the classes and done a significant amount of labwork ought to have had no trouble getting 24 marks.

Question 1

As a general observation, this was reasonably well done compared to questions 2 and 3 which many people did not answer at all. However, I was concerned that many people gave answers which were quite obviously copied out of a textbook. In some cases this was obvious because it used terminology which I doubt (from the rest of their paper) they understood. In a few they copied out material referring to things we haven't even covered. Copying material without understanding it is not a good approach to questions like this. Firstly, the final exam won't be open book. But secondly, and more importantly, the whole point of a test like this is to test your understanding. I hope that no-one goes into the final exam having "revised" by memorising things without understanding. As you can see from past exam papers, very few marks in this subject can be gained by reproducing things from memory, and it is surely more laborious to actually sit down and memorise a whole lot of things than it is to understand them so your answers come naturally from your understanding.

Part a

Many people seemed to misunderstand this question and answered it as if it were "what is an object?". An object is not a type, of course (but an object has a type), so "what is the type object" is a nonsense phrase. But "what is the type Object" is not. Note this font (which has the name courier) is consistently used in the notes, in many textbooks, in the tests, and will be in the final exam, for wording that comes directly from a Java program, and not for anything else. So that means Object must be a word that can be used in a Java program. There is no excuse for not knowing what is means and how it is used: we covered it in week 18, it is covered prominently in the lecture summary for that week, and it was used in some of the bank examples which were talked about in the lectures and you were expected to study until you understood them.

Part b

A similar problem to part a occurred here. Many people answered the question as if it were "what is a character?". You ought to be aware that char is the word used for the ordinary character type in Java, so Character is something different. It is one of the primitive wrapper types, which were clearly covered in the lectures and are mentioned in the week 18 lecture summary.

Part c

Note that we, as in the class for Introduction to Programming at Queen Mary, used the term "inner class" to mean any class defined within the scope of another class. So writing this and then adding (as many people did) "we use the term to mean a non-static nested class" does not make sense, as it defines the term "inner class" first to mean one thing, and secondly to mean something else (a subset of the first thing). Also to define "inner class" as a "non-static nested class" simply means using one term to define another. If you were to choose this more restricted definition of the term "inner class" (as some authors, for example David Barnes do), then you also need to explain what a "nested class" is as well. It is obvious that those who wrote "we use the term to refer to non-static inner classes" copied this definition from the glossary of David Barnes's book, where it is given, without understanding what it meant - "we" in that context meant "David Barnes".

Part d

Although the question clearly stated "the keyword interface", some people still answered it as if it was "what is an interface?". It is unfortunate that we have the general term "interface" in computing, which we use in phrases like "graphical user interface", but in Java also the much more specific concept of an interface defined by using the keyword interface. However, the wording and Courier font used for the question made it very clear it was the latter meaning that was intended, and this was covered in week 11, you can find it mentioned in the lecture summary for that week, and extensive use of it has been made in the course since then.

Note that it was not certainly enough to say "we use the word to declare a Java interface", since that really does not indicate an understanding of what is meant by the term. "We use it to define a type in terms of what objects of that type can do" is better, but a little imprecise, so a completely correct answer would need to mention that an interface lists the methods that all objects of that type are expected to be able to have called on them.

Part e

Many people answered this question only with respect to static fields, or static methods. You can also have static inner classes. A completely correct answer would need to cover all of these, linking them together with the general idea of static as indicating that an entity isn't associated with a particular object created from a class. Some people mentioned static initialiser blocks, which we haven't covered at all in the course. It would be acceptable to mention static initialiser blocks if you just wanted to be complete in your answer and demonstrate reading beyond what was in the course, but mentioning just this use and not the main use of the keyword static suggests just a random copying without understanding of a phrase from a textbook.

Question 2

Many people skipped this question altogether. It really was not complicated - it just asked you to make use of methods from a class given to you without having to know what is in those methods. We did this right at the start in week 3 with the Ship class, and in the Grommet exercises in week 6. Those who did badly when they were tested on being able to do this in test 1 ought to have learnt from experience and made sure they had corrected their misunderstandings. That is one of the main reasons for having term-time tests. If you do badly in a test you need to go over it, work out what you didn't understand, and make sure you do understand it in future.

One big difference here from what we did before, however, is that questions 2 and 3 deal with arrays of objects. That ought not to have been a problem since we dealt extensively with arrays of objects in the banks examples in class. Question 3 also deals with a class (Councillor) which has a subclass (Alderman). Again, we dealt with something similar in the banks example (AccountB with subclass DepositAccountB).

Many people still don't seem to understand the basic idea of methods, parameter passing and value returning. When you are asked, for example to "write a method which takes a Thing and a Doodah and returns a Grommet", you ought immediately to start by writing a header something like:

Grommet myMethod(Thing t, Doodah d)
Here, myMethod is just a name you've chosen to call the method, you might be asked to call a method a given name, otherwise you should choose a name which fits in with the intended use of the method. You have to give names to your arguments, here they are just t and d. If there are more meaningful variable names you can think of, then you should use those. For example, in this question all the methods take an array of Councillor objects as an argument. It is much better to name this argument council than something meaningless like a.

Note that if we have a class called MyType, then the type MyType[] is the type for an array of MyType objects. This is true for any type, so if an argument is to be an array of Grommet objects, then its type is Grommet[], for example.

Suppose the method we started above called myMethod is static, so its full signature is:

public static Grommet myMethod(Thing t, Doodah d)
and suppose it's in a class called MyClass. Then if t1 is a Thing value and d1 is a Doodah value (they could be variables of type Thing and Doodah, but they could also be method calls that return values of that type), then MyClass.myMethod(t1,d1) is a Grommet value. The exact value is given by executing the code which follows the { after the signature given above, but initially setting t in the code to t1 and d in the code to d1. Since object assignment is aliasing, that means that t and d are local names for t1 and d1. The value which the call evaluates to is the value given in the return statement that is executed in the method's code.

If the method is not static, then its full signature will be:

public Grommet myMethod(Thing t, Doodah d)
Now, as it is in class MyClass when it is called it must be attached to a MyClass object. If m is a myClass value, then m.myMethod(t1,d1) is a Grommet value. The value is given by executing the code of the method as before, but the word this in the code should be considered replaced by m (remember this means "the object this method is attached to). Also if there are any calls to non-static methods or mentions of non-static fields belonging to class MyClass in the code for myMethod, then in this call they should be considered as attached to m because the call they are in is attached to m.

Part a

A minor problem in a lot of answers that were otherwise correct was a failure to check whether councillors were present before their votes were counted. The question states that councillors present at the time vote either to agree or disagree, and a method is given to test whether a councillor is present. Note that because of this, it is not necessary for a motion to get the agreement of more than half the total number of councillors, only for more of those present to agree to it than to disagree to it. The code for this involves the standard technique of running an index variable from 0 to the length of the array, considering each element of the array indexed in turn. In this case, the consideration involves first checking whether the councillor is present (by calling the isPresent method on the Councillor object indexed by the index variable i), and if so adding the councillor's vote (obtained by calling the agrees method) to a total counting the yes and no votes.

One surprisingly common error, which is obviously wrong if you think about it, was to write something like this:

for(int i=0; i<council.length; i++)
   if(council[i].isPresent())
      if(council[i].agrees(m))
         return true;
      else
         return false;
Why is this wrong? Because whenever a return statement is executed, execution of the method is halted and whatever value is given with the return statement becomes the value of the call as a whole. In this case it means that as soon as a councillor is found who is present, the councillor's vote on the motion is found and just that one vote is returned as the value of the whole method, no other councillors' votes will be counted. It's common enough in a loop to find a statement of the form if(test) return true; or if(test) return false; where test is some test. This just means that we have found something that means we know the method should return true or false without having to go through the rest of the loop (for example if we were checking for the presence of a particular item in an array - once we have found it we don't need to cary on checking the rest of the array). But if you find yourself writing something like if(test) return true; else return false; you've almost certainly made a mistake because either one way or the other it will halt the loop and the method it is in prematurely.

For some reason, some people thought you would have to use the method getName somewhere in this answer, for example council[i].getName().agrees(m). This would attach the call agrees(m) to the string returned by getName() and not to the Councillor object. Obviously strings can't agree with motions, so that doesn't make sense.

Part b

The only difference between the answer to this part and the answer to part a was to remember to use the instanceof test to test whether a Councillor object in the array was also an Alderman object. Remember that as Java is an object-oriented object, an object may have several types. If we have class Doodah extends Thing, for example, then every object of type Doodah is also of type Thing (but not every object of type Thing is of type Doodah). If t is of type Thing, the test to see if it is also of the more specialist type Doodah is t instanceof Doodah. I was surprised that a number of people wrote insteadof rather than instanceof, though I was willing to excuse it as just one of those mistakes made when you mean to write one thing but find yourself writing something else.

Note something similar was done with the banks example where we went though an array of AccountRecord objects looking for those which had DepositAccountB objects in them so we could call the method update on them. This shows the importance of looking through the programs that were given as examples in class and making sure you understand them and how they work. Ask me if you don't. All the examples were given for a reason: to illustrate principles which you need to know.

Again, some answers to this question showed some strange uses of getName. The name of the councillor is just an extra feature, and the question does not suggest it has anything to do with whether the councillor is a member of the executive.

Part c

The method that answers this question is an example of a void returning method. This means a method which when you call it doesn't return a value. Instead it does something, in some case that might be to print or display something, in others it might be to change the value of the object the method is attached to, or an object given to it as an argument. It is very important that you understand the difference between a method which returns something and a method which prints something. If a value is printed, you can see it on the screen but it can't be used any further than that. If a value is returned it means it can be passed on to be used somewhere else in the program. Obviously it is far more useful to have a method which returns something you can use elsewhere (including passing to System.out.print to print it), than one which just prints something, so on the whole never write a method which prints a value rather than returns a value unless you really are asked to write a method to print something.

Since the method you are asked to write in part c is static, it won't be called attached to an object. So it will change the value of the array passed to it as an argument (by setting one of the array cells that held a Councillor object to hold an Alderman object).

Note that the test for whether two strings, str1 and str2, are equal is str1.equals(str2). It is not str1==str2 because strings are objects and the == test tests whether two object references are to the same object. It evaluates to false if it is applied to two object references which aren't to the same object, even if they are to diffeent objects with the same content. Another way to test two strings are equal is str1.compareTo(str2)==0, since the compareTo method returns a negative number if str1 is less than str2 in alphabetic order, a positive number if it is greater than str2 alphabetically, and 0 if they are equal in content.

Question 3

Continues on from question 2, so much of the comments about that also apply to this. Again, you need a feel for how types and methods and objects fit together, which everyone ought to have as it is the major part of what the course is about, but some obviously still haven't.

Part a

Most of those who attempted an answer to this took the hint that was given in the lecture beforehand about the use of this to mean "the object the method is attached to". As the question here specifically says that the method is to be called on a Motion object, it can't be static. The Motion object it is called on is referred to as this inside the code for the method, and has to be referred to in this way in order that it can be the argument to the call to the method agrees attached to Councillor objects.

In this question, the phrase "assuming all councillors were present" was given, indicating that unlike parts a and b of question 3, there should not be a check using the isPresent method on the Councillor objects.

Part b

This question was deliberately left vague so that it could be answered either by returning a boolean (presumably true if the first motion is more popular than the second, false otherwise), or a reference to the more popular motion. Also, it could be static, in which case when it's called it's not attached to any Motion object, so the Motion objects being compared are both given as arguments to it, or it could be non-static, in which case one of the Motion objects it is comparing is the one it is attached to, the other is an argument to the method. The council must also be given as an argument in all cases (with the type Councillor[] i.e. array of Councilor objects). In this question, like parts a and b if question 2, it didn't really matter how you dealt with motions getting an equal number of votes, as the question didn't tell you how to deal with them.

The question gave the hint that you could use the method from part a, and this made it much simpler to answer. Despite this, some people gave answers in which code very similar to that of their answer to part a was repeated in order to count the votes first for one motion then for the other. It is always permissible in a programming exam to save time by making use in one part of a method you've defined in another, should that be appropriate.

Part c

Most people who answered this question took the hint that their answer should include a use of the method defined in part b. Obviously, the answer given here depended on the answer to part b. For comparison, here's answers to parts b and c using the idea that the comparison of two motions method returns a reference to one of them rather than a boolean, and also making that method static. Both of these are the other way round from the solutions given in the model answers:
public static Motion morePopular(Motion m1, Motion m2, Councillor[] council)
{
 if(m1.countVotes(council)>m2.countVotes(council)
    return m1;
 else
    return m2;
}

public static Motion bestMotion(Motion[] motions, Councillor[] council)
{
 Motion best = motions[0];
 for(int i=1; i<motions.length; i++)
    best = Motion.morePopular(best,motions[i],council);
 return best;
}
You can see the idea here is to have a variable referring to the best motion found so far. Initially it's set to the first motion, but it is set to other motions if more popular ones are found as a check is made across the array of motions. Note that in the solution given in the model answers, the best variable held the location in the array of motions of the best motion found so far, rather than a direct reference to it.

A number of people thought as the question asked for a reference to the most popular motion, the name of the method should be something like reference. That is to misunderstand why when we are being precise we use the term "reference" in Java. We could say the method just returns the most popular motion from the array, but actually it returns a reference because all Java variables except those of the primitive types hold references to objects rather than the objects themselves. That is why you can have two object variables referring to the same object, because they don't actually hold the object itself, they hold a reference to it. We sometimes show this in diagrams by showing the actual object as one thing, and the reference to it as an arrow pointing to it.

Question 4

This question was generally done better than questions 2 and 3. One problem that occurred quite a few times was people not making it clear what was intended to be working-out and what was intended to be the final answer to the question. So, for example there might have been two values given for one variable. Although it's fine to show working-out in questions like this, if you do you should always clearly separate it from the final answer.

Some people drew diagrams with two arrows coming from a box. It's not possible for a single Java object field or variable to refer to two objects at once, although of course an object can be referred to by several different references. I was not sure if the people who gave answers like this actually believed that a field could refer to two objects at the same time, or it was meant to be working out.

Some people drew cells which were initially part of the structure, but which nothing mentioned in the question pointed to after the execution of the code fragment in the question. This should not be done. If nothing points to a cell, it should be considered to have disappeared. To be technical, it is automatically garbage collected. "Garbage collection" refers to letting cells be reused. In Java (but not in some other language, such as C and Pascal) garbage collection is done automatically, so once a cell is not referred to by anything, the computer memory that was used for that store could be reused any time more memory is required when another cell is created.

Another common problem was not showing where cells were shared. In some cases, from their answers it looked as if people knew that sharing occurred, but they just didn't show it in their diagrams, in other cases they perhaps mistakenly believed that assigning one object variable or field to another meant the object was copied rather than shared.

It is important, for example, to distinguish:

from

Although both represent cases where n1 represents the list [x,a,b] and n2 represents the list [y,a,b], they represent different situations, and this needs to be made clear in the diagrams. Consider what would happen if following this, the statement n1.next.first='z' were executed. In the first case, n1 would then represent [x,z,b] and because of the sharing n2 would then represent [y,z,b]. In the second case, while n1 still comes to represent [x,z,b], n2 stays representing [y,a,b] because its cell holding 'a' is a different cell from the one which holds the 'a' for n2.

For those who haven't understood how sharing happens, suppose our initial situation is:

and then we execute n2.next=n1.next. The situation will then be as in the first diagram above, and not as in the second. This is because the next fields of the list cell objects are of object types (the same type as the list cell), and when an object field or variable is assigned an object value, it causes aliasing to occur, as we saw in week 4. So here n2.next becomes an alias for n1.next, in other words it points to the same cell that n1.next pointed to (and n1.next still points to that cell).

Part a

The main point here was to show that you understood how cells become shared with linked lists. So it was important to show what n2 points to as well as n1. Although what n2 points to wouldn't be changed by the code fragment being executed, it was necessary to show it, in order to indicate that part of it becomes shared by n1's list.

Part b

Here it was important to note that sharing does not occur after execution of the code. In this example, the first field in cells is of type int, which is a primitive type. When you assign the value of one primitive type field of variable to another, the value is copied into the other, no aliasing is set up. So, executing n2.next.first=n1.next.first simply copies the 3 from the second box of n1's list into the second box of n2's list.

Following this, the += is just the normal += of Java, where a+=b is shorthand for a=a+b. So n1.next.first += n1.first is shorthand for n1.next.first = n1.next.first+n1.first, in other words, the contents of the first cell in n1's list is added to the contents of the second cell of the list.

Part c

A common mistake made in answering this question was not to notice that the first line was one of those for loops without a body. You can tell that because it ends in a semi-colon. If there was no semi-colon at the end of that line, then the statement following it would be the body of the loop, so the loop would involve moving ptr down the list doubling the values of each cell. However the semi-colon at the end of the first line means that the statement ptr.first *= 2; is not inside the loop. Another hint to this was the fact that this line was not indented (i.e. extra spaces put at the left-hand side). Code given in an exam will always be properly indented (unless there is a deliberate reason not to). Also the code you write as answers should always be properly indented. This is because although the Java compiler doesn't take any account of how the code is laid out, so the indentation doesn't make any difference to the meaning of the code, it makes it much easier for the human reader to be able to tell the structure of the code at a glance.

So this code moves ptr down the list represented by n1, until it points to a cell whose first field is not less than 8. After that, the statement ptr.first *= 2; is executed, doubling the value of that cell whose number is not less than 8 (i.e. changing 9 to 18). The next statement sets the next field of that same cell to point to the cell that n2 points to, so causing sharing of cells. The final statement triples the value of the number in the second cell of n2's list, and due to cell-sharing that tripling is shared because that cell is now also the fifth cell of n1's list.

Part d

In this case, the second line is inside the loop, indicated to the Java compiler by no semicolon at the end of the loop header, and also to us by the second line being indented three spaces. The statement inside the loop creates a new cell, with number 0, for the next field of the cell ptr is pointing to to point to. But that new cell's next field is the old next field of the cell ptr was pointing to, so the result is to insert a 0 into the list. Note that the update part of the for-loop header moves ptr two places down the list, this means each time round the list it is moved past the new 0 that was inserted.

The final line of the code in this example is not in the loop. We can tell this because the loop body does not begin with {, so only the first statement following the loop header is in the loop body. It is also indicated (to us, not to the Java compiler) by the fact that the third line is not indented. The final line causes the next field of the cell n1 points to to point to the same cell that the next field of the cell n2 points to points to. Or (to use less convoluted grammar) it makes n1 share all but the first cell of the list n2 refers to.

Part e

The first line here introduces a new variable called k which holds int values. There are two statements in the body of the for-loop, and we can easily tell this by the way they are bracketed together by opening and closing curly brackets. Note this is a slightly confusing for-loop, because although the initialisation part initialises ptr and the update part changes the value of ptr, the test part tests not ptr but n2, whose value is changed in the loop body. This was not a mistake. We could alternatively have written the loop:
for(ptr=n1; n2!=null; n2=n2.next)
   {
    k += n2.first;
    ptr=ptr.next;
   }
or we could have written it using the equivalent while loop:
ptr=n1;
while(n2!=null)
   {
    k += n2.first;
    n2 = n2.next;
    ptr=ptr.next;
   }
The effect is that both n2 and ptr are moved down their lists. As nothing else points to the list n2 initially pointed to, that list is lost, but k holds the sum of its elements. Since ptr was initially set to the list n1 refers to, and that list was longer than the list n2 initially referred to, when the loop is exited with n2 having the value null, ptr still points to a cell, which happens to be the last cell of the list n1 refers to. So the last line sets the number of that cell to the sum of the elements that were in the list which n2 initially referred to, i.e. 17.

Matthew Huntbach
6th April 2001