Exam One Sample Solutions

Q1. [16 points] This question tests your basic knowledge of the Java for statement. Convert the following sequence of statements into a for statement where the invocation of System.out.println only appears once.

+4 Having syntax of for loop:

for (INIT ; TEST; ADVANCE) {
    COMPUTATION
}

+3 Valid INIT
+3 Valid TEST
+3 Valid ADVANCE
+3 Valid use of System.out.println (...)

There are so many possible solutions. Here are three that I found interesting (the first is the way I would have done it)

for (int i = 8; i >= 0; i = i -2) {
   System.out.println ( i*i );
}
int []ar = {64,36,16,4,0};
for (int i = 0; i < ar.length; i++) {
  System.out.println (ar[i]);
}
for (int i = 64; i >= 0; i--) {
  if (i==64 || i == 36 || i == 16 || i == 4 || i ==0) {
    System.out.println (i);
}

Q2. [20 points] Please circle either TRUE or FALSE for each statement.

Note because of the large class size and the inherent visual problems with T/F exams, there were two exams printed. All answers are here:

To instantiate an array of 8 boolean values use the Java statement:

                     boolean [] ba = new boolean(8);
 FALSE -- note that the 8 must be in [8].
Assuming that d is a variable of type double, while i is a variable of type int, the following statement is valid:

                   i = d;
FALSE -- double has too much information and must be manually cast to an int.

Alternate questions were as follows:

To instantiate an array of 8 boolean values use the Java statement:

                     boolean [] ba = new boolean[8];
 TRUE
Assuming that d is a variable of type double, while i is a variable of type int, the following statement is valid:

                   d = i;
TRUE -- can always cast an int to a double

The following questions were the same on both tests.

To overload a method, a programmer simply declares that a method can throw an Exception  FALSE -- overloading is to have another method with a different parameter list
Given a rectangle with int sides x and y as shown, the perimeter is calculated by the Java statement:

                    int p = 2*x+y;
 
FALSE -- the proper expression is 2*(x+y)

 
Q3. [20 points] There are at least five logical errors in the following code that compiles successfully. The code is supposed to read in two numbers and then print them out in increasing order. Instead, after following the instructions given by the prompt, the program throws an Exception and terminates with no output. Circle where the error(s) occur in the code.

Each logical error was worth four points. There are six in the program

  1. Note how main defines a local variable int[] list which shadows the static variable with the same name. Thus, when you get to the order() method, list is undefined. SOLUTION: remove the local variable from main.
  2. The Scanner class requires you to input numbers with whitespace between them, but the comment asks the user to enter the numbers separated by commas. SOLUTION: change the text to ask for space
  3. Note how order is intended to swap the location of two array elements, however, when called with low=1 and high=2, these are outside of the size of the array; low should be set to zero and high should be set to one. (WHILE THIS WAS REALLY ONE LOGICAL ERROR, I ALLOWED IT TO COUNT FOR TWO)
  4. Note that the System.out.println() method first has the two ints with the "+" operator. Thus this will actually add the two numbers together, rather than showing them in increasing order.
  5. Note how the if statement in the order method is properly indented BUT it won't work because there are no "{" ... "}" around the three nested statements. If you were to run this code, it would not work!
import java.util.Scanner;
public class Test {
  static int []list;
  static int tmp;

  static void order (int a, int b) {
    if (list[a] > list[b])                 // NOTE LACK OF { ... } AFTER IF STATEMENT
      tmp = list[a];
      list[a] = list[b];
      list[b] = tmp;
  }

  public static void main (String []args) {
    Scanner sc = new Scanner (System.in);
    System.out.println ("Enter two numbers separated by commas");
    int []list = new int [2];
   
    int low = 1;
    int high = 2;
   
    list[low] = sc.nextInt();
    list[high] = sc.nextInt();
   
    order (low, high);
   
    System.out.println (list[0] + list[1] + " are increasing.");
  }
}

Q4. [20 points] Given the following code, fill in the diagram with appropriate references. Show all objects, variables (static, instance, and local) and parameters including values or references as appropriate. Note that the program is stopped at the comment. 


public class
Value {
 public int x;

 Value next;

 public Value (int x) {
   this.x = x;
   next = null;
 }
}


public class Sample {
  static Value inspect (Value p) {

    if (p.x < 5) {
      return p;
    }
    return null;
  }

  static void update (Value x, Value y) {
    x.next = inspect(y);

    // STOP AND DRAW HERE.
  }

  public static void main (String args[]) {
    Value p = new Value (5);
    Value q = new Value (3);
    update (p, q);
  }
}

+4 Having boxes for p and q in the local variables section of main, and having arrows coming out of them pointing (one each) to the objects
+2 labeling lower box main
+2 having an argument args in the argument to main
+2 having second box labeled updated
+2 having x and y arguments within update properly pointing to the objects, x to the object pointed to by p and y to the object pointed to by q.
+4 for having the object pointed to by p to have an x box with "5" inside it, and a next box pointing to the other object.
+4 for having the object pointed to by q to have an x box with "3" inside it and a next box pointing to null

Q5. [2 points] fill in this Dilbert cartoon. Extra points if I fall out of my chair laughing.

Q6. Design an append method for the List class that maintains an ordered list of int values (always appending to the end)  but ensures that an Exception is thrown if one tries to append an int already in the list.

         (a) [6 points] Write the declaration for append.
(b) [8 points] Write the method implementation for append.
(c) [8 points] Describe in English the test cases for append.

 

public class Node {
 int x;
 Node next;

 public Node (int x) {
   this.x = x;
   next = null;
 }
}
public class List {
 Node  head;
 public List () {
   head = null;
 }
}

 

(a) [6 points] public void append (int i ) throws Exception { ... }

+2 public void append
+2 int i as an argument [also allowed Node n]
+2 throws Exception

Note that append should not be a method of the Node class. Note that it is inappropriate for the append method to be a static method of the List class. For example, if you made a method 'public static void append (List l, int i) throws Exception' this would be syntactically correct, but you would be missing out on the opportunity of using an instance method.

Tangent: This yellow box is not required for answering the problem; I wanted to show a point.s
For example, consider the difference in these three methods whose purpose is to count the number of Node objects in a linked list.
// Defined in the Node class
public class Node {
  int x;
  Node next;

  /**
   * Return count of Nodes in list
   * starting with this Node. */ 
  public int count () {
    int ct = 1;       // count self
    Node ptr = next;
    while (ptr != null) {
       ct++;    // count ones after
 
      ptr = ptr.next;  // ADVANCE
    }

    return ct;
  }
}

// Defined static in List
public class List {
  Node head;

  /** Return number of Nodes in
   * list l. */
  public static int count (List l) {
    int ct = 0;    // might be empty
    Node ptr = l.head;
    while (ptr != null) {
      ct++;            // count node
      ptr = ptr.next;  // ADVANCE
    }

    return ct;
  }
}

// proper solution
public class List {
  Node head;

  /** Return number of Nodes in
   * our list. */
  public int count () {
    int ct = 0;   // may be empty
    Node ptr = head;
    while (ptr != null) {
      ct++;       // count node
      ptr = ptr.next;  // ADVANCE
    }

    return ct;
  }
}

(b) [8 points] Write the method implementation for append

A few students were confused as to exactly what I was asking. I used the append example in class several times (together with provided handouts). Append simply means to take a list and append something to the end of the list. There is thus an implied ordering, which is based on the order in which append was invoked. Thus if someone used append four times -- once with 2, once with 54, once with 3 and once with 10 -- then the List would have the following Nodes in the following order:

head: 2
next one in the List: 54
third one in List 3
last one in List 10

Thus there was no mandate that you were required to sort the elements as they were being ordered.

To handle append, you needed to break down the solution into its parts

public void append (int i) throws Exception {
  // what if current List (i.e., this) has no nodes and is empty?

  // verify that value doesn't currently exist in the List.
  // If it already is present, throw an Exception.

  // append new Node whose value is i to the end of the List
}

Now that we know the scope. Let's tackle each in turn:

public void append (int i) throws Exception {
  // what if current List (i.e., this) has no nodes and is empty?
  if (head == null) {
    head = new Node (i);
    return;
  }

  // verify that value doesn't currently exist in the List.
  // If it already is present, throw an Exception.

  // append new Node whose value is i to the end of the List
}

We can safely return within the if statement (and MUST do so to avoid a sneaky defect).

public void append (int i) throws Exception {
  // what if current List (i.e., this) has no nodes and is empty?
  if (head == null) {
    head = new Node (i);
    return;
  }

  // verify that value doesn't currently exist in the List.
  // If it already is present, throw an Exception.
  for (Node n = head; n != null; n = n.next) {
    if (n.x == i) {
      throw new Exception (i + " already exists in the list.");
    }
  }

  // append new Node whose value is i to the end of the List
}

Now we only need to add the append implementation.

public void append (int i) throws Exception {
  // what if current List (i.e., this) has no nodes and is empty?
  if (head == null) {
    head = new Node (i);
    return;
  }

  // verify that value doesn't currently exist in the List.
  // If it already is present, throw an Exception.
  for (Node n = head; n != null; n = n.next) {
    if (n.x == i) {
      throw new Exception (i + " already exists in the list.");
    }
  }

  // append new Node whose value is i to the end of the List. Note that
  // we know there is at least ONE Node in the list.
  Node ptr = head;
  while (ptr.next != null) {
    ptr = ptr.next;
  }
  ptr.next = new Node(i);
}

It is possible to make this a bit more efficient by combining the verify and append sections of the code, but by doing so, you run the risk of introducing a defect.

(c) [8 points] Describe in English the test cases for append.

+2 testing the append of int x into empty list (where List has no nodes)
+2 testing the append of int x into a list (of any size) that doesn't already have that integer.
+2 testing the append of int x into a list where x is the last number in the list
+2 testing the append of int x into a list where x is the first number in the list
  testing the append of int x into a list where x is somewhere in the middle of the list

Why do I bother with three cases for insert where x is in the list? Basically, you must look at the code above to see why. First, there is only one way that a number can't be in a list (right?) but there is any number of ways that one could try to append a number. When thinking about the problem, I found that the last while loop was tricky (having to remember to stop when 'ptr.next == null' not just when 'ptr == null') so I thought it was worthwhile to have two test cases to verify I got the code right. When you look at your code, see if you didn't make a similar mistake in your (b) implementation that would have allowed you to append an int that was the last one in the list.