Rice University - Comp 212 - Intermediate Programming

Fall 2002

Lecture #09 - Of Lists and Visitors


0. Recall

The functional Scheme-like list data structure, AList.

Each time we want to compute something new, we have to edit each class and add appropriate methods to each class.  

Functional List Framework

Class AList is calle the host and its method execute() is called a "hook" to the IListAlgo visitors.  Each time we want to compute something new, we simply add an appropriate concrete visitor and ask the list to call on the visitor.

2. Another look at the implementation code

 

Without Visitor

With Visitors

public abstract class AList {
     public abstract Object getFirst();
     public abstract AList getRest();

// Has a bunch of methods, each corresponds to a specific computation.

}
public abstract class AList {
     public abstract Object getFirst();
    public abstract AList getRest();

    /**
     * "Hook" method to execute any IListAlgo visitor.
     * @param algo the algorithm operating on this AList.
     * @param inp the input needed by algo to perform its task.
     * @return the output Object of algo.
     */
    public abstract Object execute(IListAlgo algo, Object inp);
}
public class EmptyList extends AList {
// Other methods elided...
 
    /**
     * Calls algo's empty case method.
     */
    public Object execute(IListAlgo algo, Object inp) {
        return algo.emptyCase(this, inp);
    }
}
public class NEList extends AList {
    private Object _first;
    private AList _rest;
// Other methods elided...
     /**
     * Calls algo's non-empty case.
     */
    public Object execute(IListAlgo algo, Object inp) {
        return algo.nonEmptyCase(this, inp);
    }
}
public class EmptyList extends AList {

// Implementation of each method listed in AList.
 
}
public class NEList extends AList {
    private Object _first;
    private AList _rest;
// Implementation of each method listed in AList.
}
public interface IListAlgo {
    /**
     * Perform an operation on the EmptyList host.
     * @param host an EmptyList
     * @param inp input needed by this method to perform the operation.
     * @return output Object of the operation to be performed.
     */
    public abstract Object emptyCase(AList host, Object inp);

}
 /**
     * Perform an operation on the NEList host.
     * @param host an NEList
     * @param inp input needed by this method to perform the operation.
     * @return output Object of the operation to be performed.
     */
    public abstract Object nonEmptyCase(AList host, Object inp);
   

Compute the length of a list

Add an abstract method called getLength() to AList.
Write concrete code for getLength in both EmptyList and NEList

Create a concrete IListAlgo visitor called GetLength.
Write concrete code for the methods  emptyCase() and nonEmptyCase().

public class EmptyList extends AList {
// Other code elided...
    public int getLength() {
        return 0;
    }
public class GetLength implements IListAlgo {
 
// Singleton Pattern;

    public Object emptyCase(AList host, Object inp) {
        return new Integer(0);
    }
public class NEList extends AList {
 
// Other code elided...

    public int getLength() {
        return 1 + _rest.getLength();
    }

    public Object nonEmptyCase(AList host, Object inp) {
     
  // Note how the recursive call is made below:
        Integer restLen = (Integer)host.getRest().execute(this, null);
        return new Integer(1 + restLen.intValue());
    }
}

Client Code

AList someList = blah-blah-blah;
someList.getLength();
AList someList = blah-blah-blah;
someList..execute(GetLength.Singleton, null);
   

Compute the minimum of a list using a helper

Add an abstract method called getMin() and helpGetMin(int acc) to AList.
Write the corresponding concrete code in both EmptyList and NEList

Create concrete IListAlgo visitors called GetMin and GetMinHelper.
Write corresponding concrete code for the methods  emptyCase() and nonEmptyCase().

public class EmptyList extends AList {
// Other code elided...
     public int getMin() {
       
throw new IllegalArgumentException("Empty list has no data.");
    }
}
public class GetMin implements IListAlgo {
    
// Singleton Pattern.

    public Object emptyCase(AList host, Object inp) {
        throw new IllegalArgumentException("EmptyList has no data!");
    }
    // non-empty case elided... 
}
public class NEList extends AList {
 
// Other code elided...

     public int getMin() {
        return _rest.helpGetMin(((Integer)_first).intValue());
    }
 }
public class GetMin implements IListAlgo {
    
// Singleton Pattern.

   
// emptyCase
elided...

     public Object nonEmptyCase(AList host, Object inp) {
        return host.getRest().execute(GetMinHelper.Singleton, host.getFirst());
    }
public class EmptyList extends AList {
// Other code elided...
   
    protected int helpGetMin(int tempMin) {
        return tempMin;
    }
    }
public class GetMinHelper implements IListAlgo {
    
// Singleton Pattern.
 
    public
Object emptyCase(AList host, Object inp) {
        return
inp;
    }

 
  // non-empty case elided ...
}
public class NEList extends AList {
 
// Other code elided...

      protected int helpGetMin(int accMin) {
        return _rest.helpGetMin(Math.min(((Integer)_first).intValue(), accMin));
    }
}
class GetMinHelper implements IListAlgo {
    
// Singleton Pattern.
 
     // emptyCase elided...
 
    public Object nonEmptyCase(AList host, Object inp) {
       // Note how and why type casting is used below:
        int f = ((Integer)host.getFirst()).intValue();
        int i = ((Integer)inp).intValue();
      
// Note how a recursive call is made below:
        return host.getRest().execute(this, new Integer(Math.min(f, i)));
    }

Client Code

AList someList = blah-blah-blah;
someList.getMin();
AList someList = blah-blah-blah;
someList..execute(GetMin.Singleton, null);
   

 

2. Iterator Pattern

Procedural (read non OO) programming often requires traversing a data structure in some way in order to process the data elements in the structure.  The coding pattern looks as follows.

while (structure has more elements) {
    get next data element from the structure and process it;
}

In order to hide the details of implementation of the data structure but still provide a way to "iterate" through the data structure, we should provide an object that has intimate knowledge of the structure and knows how to traverse it.  Such an object is called an iterator of the structure.  Java provide two interfaces that serve to iterate through a collection of data elements: java.util.Enumeration and java.util.Iterator.  See the Java API for more details.

We now give an example of an Enumeration for our list structure, though our structure already has all the necessary behaviors for any client to traverse it.

ListEnumerator.java
Created with JBuilder
import java.util.Enumeration;
import OOscheme.*;

/**
 * Enumerates the objects contained within a list.  Also serves as a visitor
 * to check the host list for emptiness.
 * Serves an an example of visitor with states.
 * Illustrates the use of multiple inheritance.
 * @author Alan Cox
 */
public class ListEnumerator implements Enumeration, IListAlgo {
    private AList _list;

    /**
     * Initializes this Enumeration with the AList to be enumerated.
     * @param list the AList to be enumerated.
     */
    public ListEnumerator(AList list) {
	_list = list;
    }

    /**
     * Returns true if and only if their are more elements to
     * enumerate.  Otherwise, returns false.
     * @return boolean
     */
    public boolean hasMoreElements() {
	return ((Boolean)_list.execute(this, null)).booleanValue();
    }

    /**
     * Returns the next element.
     * @return Object
     */
    public Object nextElement() {
	Object first = _list.getFirst();
	_list = _list.getRest();
	return first;
    }

    /**
     * Returns Boolean.FALSE to indicate that the list has no more
     * elements to enumerate.
     * @param host an EmptyList
     * @param inp not used
     * @return Boolean
     */
    public Object emptyCase(AList host, Object inp) {
	return Boolean.FALSE;
    }

    /**
     * Returns Boolean.TRUE to indicate that the list has more
     * elements to enumerate.
     * @param host an NEList
     * @param inp not used
     * @return Boolean
     */
    public Object nonEmptyCase(AList host, Object inp) {
	return Boolean.TRUE;
    }
}


dxnguyen@cs.rice.edu
Copyright 2002, Dung X. Nguyen - All rights reserved.