Comp202: Principles of Object-Oriented Programming II
Fall 2007 -- Lecture #3: Higher Order Functions   


Today's Menu:

Abstraction of Data Movement

In many situations, processing a list involves processing the first element of a list (in the case of a non-empty list) and moving the result along the list structure in a sequential manner.  The previous two examples (reversing a list and concatenating two lists) illustrate two common data movements:

These two common ways of list processing can be abstracted into

Abstract function of a variable number of parameters


// NOTE the variable arguments ("varargs") syntax in Java 5.0 here!

interface ILambda {
    Object apply(Object... params);
}

Examples

1. Adding an open-ended number of Integer ogbjects.

2. "Consing" a list to form a new list.

package fp;
/**
 * Adding two Integers
 * @author DXN
 */
public class Add implements ILambda {
    public static final Add Singleton = new Add();
    private Add() {
    }
    /**
     * Adds zero to many Integer.
     * @param params Integer[].
     * @return Integer
     */
    public Object apply(Object ... params) {
    // params is treated as Object[].
//  Integer[] p = (Integer[])params; // Not allowed in Java: Class Cast Exception!
        int sum = 0;
        // New Java 5.0 for loop:
        for (Object i: params) {
            sum += (Integer)i;  // auto-unboxing: ((Integer)i).intValue()
        }
//        Traditional for loop (works OK):
//        for(int i = 0; i < params.length; i++) {
//            sum += (Integer)params[i];
//        }
        return sum;  // auto-boxing: new Integer(sum)
    }
}
package fp;
import listFW.*;
import listFW.factory.*;
/**
 * Makes new NEList given a first and rest.
 * @author DXN
 */
public class Cons implements ILambda {
    private IListFactory _fac;
    public Cons(IListFactory fac) {
        _fac = fac;
    }
    /**
     * Creates an INEList with a given first and rest.
     * @param params[0] the first element of the IList to be created.
     * @param params[1] IList, the rest of the IList to be created.
     * @return INEList
     */
    public Object apply(Object... params) {
        return _fac.makeNEList(params[0], (IList)params[1]);
    }
}

 

Forward Accumulation Visitor: Foldl "fold left"

Uses an ILambda to process a list from front to end.

Functional definition:

IList (Immutable List) LRStruct (Mutable list)
public class FoldlIList implements IListAlgo {

    private ILambda2 _f;

    public FoldlIList(ILambda f) {
        _f = f;
    }


public class FoldlLRS implements IAlgo {

    private ILambda2 _f;

    public FoldlLRS(ILambda f) {
        _f = f;
    }

// empty case: in class exercise
public Object emptyCase(IMTList h, Object... b) {
    return b[0];
}
// empty case: in class exercise
public Object emptyCase(LRStruct h, Object
... b) {
   // class exercise
}
// non-empty case: in class exercise
public Object emptyCase(INEList h, Object... b) {
  return h.getRest().execute(this, _f.apply(h.getFirst(), b));
}

}

// non-empty case: in class exercise
public Object emptyCase(LRStruct h, Object
... b) {
   // class exercise
}

}

Reverse Accumulation Visitor: Foldr "fold right"

Uses an ILambda to process a list from end to front.

Functional definition:

IList (Immutable List) LRStruct (Mutable list)
public class FoldrIList implements IListAlgo {

    private ILambda2 _f;

    public FoldrIList(ILambda f) {
        _f = f;
    }


public class FoldrLRS implements IAlgo {

    private ILambda2 _f;

    public FoldrLRS(ILambda f) {
        _f = f;
    }

// empty case: in class exercise
public Object emptyCase(IMTList h, Object... b) {
   // class exercise
}
// empty case: in class exercise
public Object emptyCase(LRStruct h, Object... b) {
   // class exercise
}
// non-empty case: in class exercise
public Object emptyCase(INEList h, Object... b) {
   // class exercise
}

}

// non-empty case: in class exercise
public Object emptyCase(LRStruct h, Object... b) {
   // class exercise
}

}

Food for thoughts: Rewrite Reverse and Append using Foldl and/or Foldr

Study the following JUnit test code to see how reversing a list is done using FoldlIList.

package listFW.test;
import junit.framework.TestCase;
import listFW.*;
import listFW.visitor.*;
import listFW.factory.*;
import fp.*;
/**
 * Testing reversing a list using Foldl.
 * @author DXN
 */
public class Test_FoldlIList extends TestCase {
    IListFactory fac = CompositeListFactory.Singleton;
    ILambda cons = new Cons(fac);
    IListAlgo algo = new FoldlIList(cons);
    IList mt = fac.makeEmptyList();
    
    public void testEmpty() {
        assertEquals("Reverse Empty list", "()", mt.execute(algo, mt).toString());
    }
  
    public void testNonEmpty() {
        IList L = fac.makeNEList("a", mt);
        assertEquals("Reverse (a)", "(a)", L.execute(algo, mt).toString());
        L = fac.makeNEList("b", L);
        assertEquals("Reverse ( b, a)", "(a, b)", L.execute(algo, mt).toString());
        L = fac.makeNEList("c", L);
        assertEquals("Reverse ( c, b, a)", "(a, b, c)", L.execute(algo, mt).toString());
    }
}

 

As one can see from the above, to reverse a list, all we have to do is fold left using "cons" as the processing function.

We leave it as an exercise to concatenate two lists using an appropriate fold operation together with an appropriate ILambda.

 

Mutable Binary Tree Framework

The Mutable Binary Tree Framework stores data in a hierarchical fashion, where mutation is allowed:

 


Last Revised Thursday, 03-Jun-2010 09:52:32 CDT

©2007 Stephen Wong and Dung Nguyen