Relevant Reading Material

TBD

Chapters from How to Design Classes (HtDC)

TBD

The many ways to iterate

Recall our generic List<X> from last lecture

genericListInterface

for -loop

We can iterate over an instance of our generic list by using size and a for -loop

Fragment of a Main class using our generic list
public static void main(String[] args) {
    // empty lines for more visible output in IntelliJ's Console
    System.out.println();
    System.out.println();
    System.out.println();
    List<Integer> intList = List.createEmpty();
    intList = intList.add(1).add(10).add(100);

    System.out.println("\tPrint list as is: " + intList);
    System.out.println("\tPrint list joined with \",\": " + intList.join(SEPARATOR));

    System.out.println("\nUsing for-loop");
    for (int i = 0; i < intList.size(); i++) {
      System.out.println("\tFor-loop element at: " + i + " is: " + intList.elementAt(i));
  }
}
Output
	Print list as is: (100(10(1)))
	Print list joined with ",": 100, 10, 1,

Using for-loop
	For-loop element at: 0 is: 100
	For-loop element at: 1 is: 10
	For-loop element at: 2 is: 1

Iterators

Iterating over a list (or a collection of objects) is a very common task. Java provides as part of the java.util package an interface called Iterator which includes the following signatures

  • hasNext() - check if there is a next element in the list

  • next() - obtain the next element in the iteration moving our iterator one element

  • remove() - remove the current element in the underlying collection default implementation throws an exception

An iterator is not the same as the list (collection) that it is pointing to. An iterator provides a view of the collection.

So let’s implement Iterator for our generic list. We will add a method on our List that creates and returns our GenericListIterator

Implementing Iterator for our List
//Inside our List interface

interface List<X> {
 ...

 Iterator<X> iterator();

 ...
}


// In a new file we define our GenericListIterator
public class GenericListIterator<X> implements Iterator<X> {

  private AList<X> list;

  public GenericListIterator(AList<X> xes) {
    this.list = xes;
  }

  @Override
  public boolean hasNext() {
    return !list.isEmpty();
  }

  @Override
  public X next() {
    try {
      X element = list.getFirst();
      list = list.getRest();
      return element;
    } catch (GenericListException glex) {
      throw new NoSuchElementException(glex.getMessage());
    }

  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException("Don't even think about it!");
  }
}

Now that we have our own iterator we can iterate over our List

Fragment of a Main class using our new iterator
...

 System.out.println("\nUsing iterator interface");
    Iterator<Integer> it = intList.iterator();
    while (it.hasNext()) {
      System.out.println("\tIterator element: " + it.next());
    }
...

And the output

Using iterator interface
	Iterator element: 100
	Iterator element: 10
	Iterator element: 1

Notice that when using our iterator we have no access to the current index. In fact we could have implemented our iterator to provide elements in any order we want. We could even provide multiple such iterators to allow different order to iterating through the elements of our collection.

foreach in Java

Notice how the code used to iterate over a collection that supports an Iterator is pretty much the same

Iterator<..> it = o.iterator(); // get the iterator

while(it.hasNext()){
    element = it.next();
    ...                  // compute using this element
}

Repetitions like these are good candidate for abstraction. Java has already done that for us through the Iterable interface that is part of java.lang. [1] If our collection implements Iterable then we can use the for-each syntax, recall

Recall the Java syntax for for-each
...

 for (X element: o) {  // o is an instance of our Collection
 }
...

This an even more succinct way to iterate over the list of a collection. So let’s extend our List implementation to implement Iterable. Thankfully we have already developed the method iterator() which is the minimum requirement needed to implement Iterable

Extending Iterable
public interface List<X> extends Iterable<X> {
   ...
}

Now we should be able to use for-each to iterate over our list

Using for-each on our lists.
 System.out.println("\nUsing iterable interface");
    for (Integer element : intList) {
      System.out.println("\tList element: " + element);

    }
Output of our code snippet that uses for-each on our list
Using iterable interface
	List element: 100
	List element: 10
	List element: 1

Iterators are not just for lists!

We can use Iterator s to present a view over a sequence, whether ordered or unordered. We can even use nested iterators (or higher-order iterators) to provide a custom view of an existing iterator, e.g.,

  • given an iterator that iterates over a collection 1 element at a time, create a new iterator that iterates 2 elements at a time, or,

  • given an iterator of iterators (a view of a list of lists) create an iterator that iterates over our list of lists as if it was a single, flat (non-nested) list.

Fibonacci numbers, again.

Here is an iterator that provides the next number in the Fibonacci sequence. The iterator keeps the last 2 numbers in the sequence and generates the next Fibonacci number.

Fibonacci iterator
public class FibIterator implements Iterator<Integer> {

  Integer previous;
  Integer current;

  public FibIterator(Integer previous, Integer current){
    this.previous = previous;
    this.current = current;
  }

  @Override
  public boolean hasNext() {
    return true; // we can always get the next fibonacci number
  }

  @Override
  public Integer next() {
    Integer newCurrent = this.previous + this.current;
    this.previous = this.current;
    this.current = newCurrent;
    return current;
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException("NO!");
  }
}

Abstracting over behaviour, again.

We’ve seen in Racket how higher-order functions can be used to abstract over behaviour making our implementations more succinct and more reusable. Let’s see how we can achieve similar benefits in Java.

Add1 to our list elements

We will attempt a series of simple operations on our elements and we will use our implementation(s) to investigate how, if at all, we can abstract over behaviour. We used a similar tactic last semester with Racket.

First let’s focus on non-generic lists like List<Integer>

Add 1 to each element
class Main {

    public static void main(String[] args) {
        List<Integer> intList = List.createEmpty();
        intList = intList.add(1).add(10).add(100);

        // ADD 1 to each element
        List<Integer> add1List = List.createEmpty();
        for (Integer element : intList) {
            add1List = add1List.add(element + 1);
        }
    }
}

We could generalize to adding any value to each element of a List<Integer> by writing a method outside of List<X>

Add n to each element
class Main {

    public static void main(String[] args) {
        List<Integer> intList = List.createEmpty();
        intList = intList.add(1).add(10).add(100);

        // ADD 1 to each element
        List<Integer> add1List = List.createEmpty();
        for (Integer element : intList) {
            add1List = add1List.add(element + 1);
        }
    }


    private static List<integer> addN(List<Integer> list) {
        List<Integer> result = List.createEmpty();
        for (Integer element : list) {
            result = result.add(element + 1);
        }
    }
}

How about,

  1. Multiplying by a number n

  2. Dividing by a number n

Even if we keep adding methods to implement these tasks, we are only dealing with List<Integer>. It is easy to imagine useful operations on lists that hold objects of some other type like

  1. Append all the strings in a list of strings to get one string as the result

  2. Join the elements of a list of string and include a separator between them

  3. Apply logical or on a list of booleans, etc.

What we are aiming for here is something like map and fold that will allow us to pass in the operation(s) that we want to perform on the elements of our list.

Functional Objects

How can we define and create a Java value such that

  1. We can pass it as an argument

  2. We can return it as a result of calling a method

  3. Contain code (much like a Racket function or lambda) that a method can call or apply to arguments

The only Java value that fits these criteria is a reference type that we can define to accept and return values of the types we want. Let’s define an interface for such a reference type.

public interface Function <I,O> {

  /**
   * Apply this funtction to {@code input} and return the result.
   *
   * @param input fucntion's input
   * @return return the result of applying this function to input.
   */
  O apply(I input);
}

The Function interface is our first attempt to define a function that takes 1 input and returns 1 output. Our input will be of some type I and our output will be of some other type O. It may be that I and O are the same type, for example a function that consumes an integer and increments it by 1 will have

  • I as Integer

  • O as Integer.

While a function that takes an Integer and returns the letter in the English alphabet at that index will have

  • I as Integer

  • O as String.

Or even a function that takes a List<List<Posn>> and creates a Bullet image for each Posn will have

  • I as List<List<Posn>>

  • O as List<List<Bullet>>.

Our Function interface guarantees the existence of apply, a method that we can use to call or apply the operation that we have encoded as an instance of Function.

Let’s code a couple of Function s

Add1 as a Function object.
public class Add1 implements Function<Integer, Integer> {
  @Override
  public Integer apply(Integer input) {
    return input + 1 ;
  }
}
MapToAlphabet as a Function object takes an integer index and returns the English letter found at index in the English alphabet.
public class MapToAlphabet implements Function<Integer, String> {

  private String[] alphabet;

  public MapToAlphabet() {
    this.alphabet = new String[]{"A", "B", "C", "D", "E", "F",
        "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
        "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
  }

  @Override
  public String apply(Integer input) {
    int alphaIndex = (input % alphabet.length) - 1;
    return alphabet[alphaIndex];
  }
}

map in Java

Now that we have a way to capture functions as objects (thus the name Functional Object or Functional Interface) we need a method on our List<X> that will take as argument a Function<I,O> and apply that function to each element of the list, returning a new list with the results. This is map

Adding a method in List<X> that will map a function over each list element.
// In List.java
public interface List<X> extends Iterable<X> {
    ...

  /**
   * Apply {@code f} to each element of the list collecting the results into a new list.
   * This is a version of the higher order function `map`.
   *
   * @param f function object to be applied to each element
   * @param <Y> type returned by f when applied to each element
   * @return the list of results obtained by applying f to each list element.
   */
  <Y> List<Y> applyToEach(Function<X,Y> f);
}


// In Empty.java

class Empty<X> extends AList<X> {

  ...

  @Override
  public <Y> AList<Y> applyToEach(Function<X, Y> f) {
    return new Empty<Y>();
  }
}


// In Cons.java
class Cons<X> extends AList<X> {

  ...

  @Override
  public <Y> AList<Y> applyToEach(Function<X, Y> f) {
    return new Cons<Y>(f.apply(getFirst()), getRest().applyToEach(f));
  }
}

Let’s use our new method.

Fragment of a Main class using our applyToEach method
public class Main {
  private static final String SEPARATOR = ", ";

  public static void main(String[] args) {

      List<Integer> intList = List.createEmpty();
      intList = intList.add(1).add(10).add(100);


      System.out.println("\nFunctional Objects: Add1: "
              + intList.applyToEach(new Add1()).join(SEPARATOR));


      System.out.println("\nFunctional Objects: MapToAlphabet: "
        + intList.applyToEach(new MapToAlphabet()).join(SEPARATOR))
  }


}
Output of our code that uses applyToEach
Functional Objects: Add1: 101, 11, 2,
Functional Objects: MapToAlphabet: V, J, A,

Recall from CS5001 the signature of map

;; map : List<X> [X -> Y] -> Y

In Racket the list on which map operates on is passed as the first argument. In Java however the list is the object upon which we call our applyToEach method which is List<X>.

In Racket we use [X → Y] in our Racket signature to denote an argument that is a function that takes one argument X and returns one result Y. In Java our code our method applyToEach takes as an argument Function<X,Y>.

In Racket map returns a Y. In Java applyToEach returns Y. [2]

fold in Java

Now that we have seen how to reproduce a map like method in Java, let’s also create a fold like method in Java.

Recall from Racket the signature for fold

fold: List<X> Y [X Y -> Y] -> Y

For fold like behaviour we will need

  • A base value Y, something that will be passed to our code as the result of the fold when we hit the empty case.

  • A function [X Y → Y] that can take two inputs

    1. An element of our collection X

    2. The current result of the fold operation up to this point (accumulator) Y

    3. Returns the new value for our accumulator Y

We can create a new functional interface that has an apply method that takes 2 arguments, or, we can add a new method in our existing Functional<I,O> interface. We will do the later.

Updated Functional interface to accommodate fold-like operations.
public interface Function <I,O> {

  /**
   * Apply this funtction to {@code input} and return the result.
   *
   * @param input fucntion's input
   * @return return the result of applying this function to input.
   */
  O apply(I input);


  /**
   * Combines the result of applying this function to {@code input} with the current accumulator
   * {@code acc}.
   *
   * @param input to this function
   * @param acc current result (result thus far)
   * @return combination of acc and result of applying this function to input.
   */

  O combine(I input, O acc);
}

Lets create a couple of classes that implement our updated Functional interface.

Add functional object for addition.
public class Add implements Function<Integer, Integer> {

  private Integer val;

  public Add(Integer val){
    this.val = val;
  }

  public Add(){
    this.val = 0;
  }

  @Override
  public Integer apply(Integer input) {
    return input + val;
  }

  @Override
  public Integer combine(Integer input, Integer acc) {
    return apply(input) + acc;
  }
}
Multiply functional object for multiplication.
public class Multiply implements Function<Integer, Integer> {

  private Integer factor;

  public Multiply() {
    this.factor = 1;
  }

  public Multiply(Integer factor) {
    this.factor = factor;
  }

  @Override
  public Integer apply(Integer input) {
    return factor * input;
  }

  @Override
  public Integer combine(Integer input, Integer acc) {
    return apply(input) * acc;
  }
}

Let’s make a new class MapToAlphabetList that is similar to MapToAlphabet but instead of returning a one element String of the letter in the English alphabet it returns a List<String>.

MapToAlphabetList functional object.
public class MapToAlphabetList implements Function<Integer, List<String>> {

  private MapToAlphabet mToA;

  public MapToAlphabetList() {
    this.mToA = new MapToAlphabet();
  }

  @Override
  public List<String> apply(Integer input) {
    List<String> result = List.createEmpty();
    return result.add(mToA.apply(input));
  }

  @Override
  public List<String> combine(Integer input, List<String> acc) {
    return acc.add(mToA.apply(input));
  }
}

Now let’s update our List<X> to include a fold like method.

foldOver implementation.
// In List.java
public interface List<X> extends Iterable<X> {

  ...

  /**
   * A fold-like operation over our list.
   *
   * @param combiner function to apply to each element and acc
   * @param base result in the case of empty list
   * @param <Y>  type of value returned by the method
   * @return the result of folding over the list with combiner.
   */
  <Y> Y foldOver(Function<X,Y> combiner, Y base);
}

// In Empty.java
class Empty<X> extends AList<X> {

  ...

  @Override
  public <Y> Y foldOver(Function<X, Y> combiner, Y base) {
    return base;
  }
}

// In Cons.java
class Cons<X> extends AList<X> {

  ...

  @Override
  public <Y> Y foldOver(Function<X, Y> combiner, Y base) {
    return combiner.combine(getFirst(), getRest().foldOver(combiner, base));
  }

}

Let’s use our new method!

Fragment of a Main class using our foldOver method.
public class Main {

    private static final String SEPARATOR = ", ";

    public static void main(String[] args) {
        List<Integer> intList = List.createEmpty();
        intList = intList.add(1).add(10).add(100);

        MapToAlphabetList mToAList = new MapToAlphabetList();
        System.out.println("\nfold over intList: MapToAlphabetList: "
                + intList.foldOver(mToAList, List.createEmpty()).join(SEPARATOR));


        Add adder = new Add(0);
        System.out.println("\nfold over inList: Sum: " + intList.foldOver(adder,0));

        Multiply mul = new Multiply(1);
        System.out.println("\nfold over inList: Product: " + intList.foldOver(mul,1));


        Integer ans = intList.applyToEach(new Add(10))
            .applyToEach(new Multiply(5))
            .foldOver(new Multiply(), 1);

        System.out.println("\nand the answer is ... : " + ans);
    }

And the output …​

Output of our code that uses foldOver.
fold over intList: MapToAlphabetList: V, J, A,

fold over inList: Sum: 111

fold over inList: Product: 1000

and the answer is ... : 3025000

Graphs

Graphs are a popular representation for a lot of common problems

  • Who is connected to you on your LinkedIn account?

  • Who are all your friends on Facebook?

  • What is the shortest route from Seattle to San Francisco if we are driving?

Graphs capture connection between entities.

The entities can be simple, like a city name, or reach like course information and with their pre-requisites.

The connections between entities in a graph can also be simple, directional like a two-way street or a one way-street and can also have properties attached to them, the distance between two cities, the cost of gas to drive between two cities or the cost of the tolls along the highway.

Graphs a little more formally.

A Graph \(G\) is defined as a pair \(G = (V,E)\) where

  • \(V\) is a set of vertices.

  • \(E\) is a set of edges.

    • An edge consists is an ordered pair of vertices \((v_1, v_2)\).

Example of a simple graph
Figure 1. Example of a simple graph

We can represent the preceding example as

\[\begin{array}{lclr} G & = & (V, E) & \\ && \text{ where }\\ V & = & \{ A, B, C, D \} & \\ E & = & \{ (A,B), (B,C), (C,D), (D,A), (D,B) \} & \\ \end{array}\]

Representing Graphs

There are different ways to capture graphs using OO.

Adjacency Matrix

The idea here is to capture the connections between vertices in a 2d matrix. We map each vertex name to an index, e.g.

\[ A \rightarrow 0 \\ B \rightarrow 1 \\ C \rightarrow 2 \\ D \rightarrow 3 \\\]

Then we can represent the graph in a matrix like so

\[\begin{array}{c|cccc} & A & B & C & D \\ \hline \\ A & \cdot & 1 & \cdot & \cdot \\ B & \cdot & \cdot & 1 & \cdot \\ C & \cdot & \cdot & \cdot & 1 \\ D & 1 & 1 & \cdot & \cdot \\ \end{array}\]

We are using the symbol \(\cdot\) to show absence, typically implementations use null.

Adjacency List

Another popular way to model a graph is using a list for each vertex to store the vertex’s direct neighbours.

adjacencylist

1. The naming gets a little confusing.
2. The mapping of Racket’s map signature is more clear when we implement applyToEach as a static method. If we choose to implement applyToEach as a static method then we also need to pass the list as an argument to our static method making the mapping to Racket’s map signature closer to being a one-to-one mapping.