Any IList object is an instance of the class Object and thus inherits the toString() method. This method simply returns the concrete class name, followed by the character '@', following by some hexadecimal code. Such a string representation of a list is not very informative. It is customary to override toString() to provide a custom string representation for a list.
A common string representation of a list displays its contents inside of a pair of matching parentheses in such a way that:
For example, the string representation of an empty list is "()", while the string representation of a non empty list containing elements 'a', 'b', 'c' in this order is "(a b c)".
To craft an algorithm for toString() to implement the above specification, we first examine a few simple examples and follow the composite design coding pattern, that is when processing a composite, call on each embedded component to process itself.
The above examples suggests defining a method for IList to return the following string representation.
This method is called a helper (or auxiliary) method for toString(). Let's name it toStringHelp(). Again as with toString(), to craft an algorithm for toStringHelp(), we first examine a few simple examples and follow the composite design coding pattern.
The above examples clearly suggest that toStringHelp() is recursive and need not call on any new helper method. The table below shows the code for toStringHelp().
interface IList | |
public String toStringHelp(); | |
EmtpyList | NEList
Object _first; IList _rest; |
public String toStringHelp() {
return ""; } |
public String toStringHelp() {
return " " + _first + _rest.toStringHelp(); } |
The toString() method simply calls on _rest to perform toStringHelp() to complete the task. This is an example of what is called "delegation". Delegation is an important paradigm in object-oriented programming. We will see more of it in examples to come.
interface IList | |
No need to declare the toString() method here since it is inherited from Object | |
EmtpyList | NEList
Object _first; |
public String toString() {
return
"()"; } |
public String toString() { //
Don't want to do all the processing here! } |
More Exercises:
Sometimes when we process the rest of a non-empty list, we need to pass to this rest some additional information in order for it to help complete the list processing. The idea here is as we traverse the list, we accumulate additional information and pass the information on to the remaining part of the list. For that reason, we call the additional information, 'accumulator'. The following algorithm, called reverse(), to reverse a list illustrates this technique.
Again to craft reverse(), we examine a few simple examples and try to decompose the task of reversing into a task of processing _fist and a task of processing _rest in the non-empty case. The idea here is to accumulate the elements of the list in the reverse order as we traverse the list and pass the accumulated reversed list to the rest to continue the accumulation. By the time we reach the end of the list, that is _rest is empty, the accumulated reversed list is the reverse of the original list.
The above suggests writing a helper method, reverseHelp(IList acc), to help reverse() complete its task. The following two tables show the code for reversing a list.
interface IList | |
public IList reverseHelp(IList acc); | |
EmtpyList | NEList
Object _first; |
public
IList reverseHelp(IList acc) {
return acc; } |
public
IList reverseHelp(IList acc) {
// "cons" _first
into the accumulator: } Remark: the call _rest.reverseHelp(acc) is the last computation in the reverseHelp(IList acc) method and thus is said to be "tail-recursive". A smart compiler will translate tail recursion to a loop in order to save time and space resulting in multifold gain in speed.
|
interface IList | |
public IList reverse(); | |
EmtpyList | NEList
Object _first; |
public
IList reverse() {
return this; } |
public
IList reverse() {
// create the list that is
the reverse of all the elements preceding _rest: } |
Recall UML diagram illustrating the general composite pattern.
As illustrated by the toString() and reverse() methods, there are many situations in which an operation is not directly recursive and may call on "helper" operations on the sub-components to carry out the task. The coding pattern for an operation that requires a helper is as follows.
class Composite extends AComponent {
// fields and sub-components.
public Object doTask (Object input) {
// Process the input parameter and the data fields to obtain a temporary
result needed by the sub-components to help complete the task:
Object temp = process (data fields, input);
// Pass the temporary result to each of the sub-components asking each of
them to "help" complete the task:
Object pr1 = _part1.helpDoTask (temp); //
partial result 1.
Object pr2 = _part2.helpDoTask
(temp);
// etc...
Object prN = _partN.helpDoTask
(temp);
//
Reassemble the partial results computed by the sub-components to obtain the
final result:
return putTogether (pr1, pr2, ..., prN);
}
protected Object helpDoTask (Object input) {
// Process the input parameter and the data fields to obtain a temporary
result needed by the sub-components to help complete the task:
Object temp = processPartial (data fields, input);
// Pass the temporary result to each of the sub-components asking each of
them to "help" complete the task:
Object pr1 = _part1.helpDoTask (temp); //
partial result 1.
Object pr2 = _part2.helpDoTask
(temp);
// etc...
Object prN = _partN.helpDoTask
(temp);
//
Reassemble the partial results computed by the sub-components to obtain the
final result:
return putTogetherPartial (pr1, pr2, ..., prN);
}
}
class Basic extends AComponent {
// fields
public Object doTask (Object input) {
// Process the input parameter and the data fields to obtain the result:
return simpleProcess (data fields, input);
}
protected Object helpDoTask (Object input) {
// Process the input parameter and the data fields to obtain the result:
return simpleProcessPartial (data fields, input);
}
}
D. X. Nguyen, last revised 01/26/05 Dung X. Nguyen - Copyright 2005 - All rights reserved