Chapter 6. Using higher-order functions

Table of Contents

6.1. Mapping
6.2. Filtering
6.3. Folding
6.4. Scanning

Higher-order functions are functions that accept other functions as parameters, or return functions as a result. In FunctionalJ, higher-order functions are defined as static methods in the Functions class.

6.1. Mapping

Mapping consists of calling a 1-parameter function on each parameter of a list of parameters and returning a list of results:

map(f, [a1,a2,..,an]) = [f(a1), f(a2), .., f(an)]

For example:

DynReflect reflect = new JdkDynReflect();

Function1<String,String> upper =
    reflect.instanceFunction(String.class, "toUpperCase").f1();

List<String> strings = Arrays.asList("abcd", "EfGh", "IJ", "pL");

List<String> uppers = Functions.map(upper, strings);
assertEquals(uppers, Arrays.asList("ABCD", "EFGH", "IJ", "PL"));

6.2. Filtering

Filtering uses a 1-parameter function that returns a boolean result to obtain, from a list of parameters, those for which the function returns true::

filter(f, [a1,a2,..,an]) = ∪ an : f(an) == true

For example:

Function1<Boolean,Integer> negative = new Function1Impl<Boolean,Integer>()
{
    public Boolean call(Integer p_value)
    {
        return p_value < 0;
    }
};
List<Integer> values = Arrays.asList(1, 5, -4, -2, 0, 7);

List<Integer> negatives = Functions.filter(negative, values);

assertEquals(negatives, Arrays.asList(-4, -2));

You can also filter to obtain the values for which the function returns false instead, using the filterNot method:

filterNot(f, [a1,a2,..,an]) = ∪ an : f(an) == false

Function1<Boolean,Integer> negative = new Function1Impl<Boolean,Integer>()
{
    public Boolean call(Integer p_value)
    {
        return p_value < 0;
    }
};
List<Integer> values = Arrays.asList(1, 5, -4, -2, 0, 7);

List<Integer> positives = Functions.filterNot(negative, values);

assertEquals(positives, Arrays.asList(1, 5, 0, 7));

To determine if at least one element of a list satisfies a condition determined by a function, you can use the Functions.any. To determine if all elements of a list satisfy the condition, you can use Functions.all:

any(f, [a1,a2,..,an]) = ∃ n : f(an) == true

all(f, [a1,a2,..,an]) = ∀ n : f(an) == true

For example:

Function1<Boolean,Integer> negative = new Function1Impl<Boolean,Integer>()
{
    public Boolean call(Integer p_value)
    {
        return p_value < 0;
    }
};
List<Integer> values = Arrays.asList(1, 5, -4, -2, 0, 7);

boolean anyNegative = Functions.any(negative, values);
boolean allNegative = Functions.all(negative, values);

assertTrue(anyNegative);
assertFalse(allNegative);

6.3. Folding

Folding takes a function of 2 parameters f, a parameter p, and a collection of elements, to successively apply the function, each time using the result of the previous function call as a parameter to the next function call. When all elements have been processed, the result of the last function call is returned.

Folding can be performed from the left or from the right. Folding from the left starts the process at the left of the collection, and places the result of the previous function call on the left side of the next function call:

foldl(f, p, [a1,a2,..,an]) = f(f(f(f(p,a1),a2),..),an)

Folding from the right starts the process at the right of the collection, and places the result of the previous function call on the right side of the next function call:

foldr(f, p, [a1,a2,..,an]) = f(p,f(a1,f(a2,f(..,an))))

Folding can also be performed without an initial parameter. In this case, the folding starts with the first two parameters at the left or at the right according to the type of fold. Respectively, the methods are named foldl1 and foldr1.

For example, to determine the maximum value from a list of values, we could fold the max function into the list to obtain the result:

List<Integer> values = Arrays.asList(1, 5, -4, 7, 0, 2);
int result = Functions.foldl1(Operators.max, values);
assertEquals(result, 7);

6.4. Scanning

Scanning is the same as folding, except that a list of each successive result is returned rather than only the final result. The scanl, scanl1, scanr, scanr1 are the counterparts of their folding equivalents.

Using scanning instead of folding in the previous example gives:

List<Integer> values = Arrays.asList(1, 5, -4, 7, 0, 2);
List<Integer> results = Functions.scanl1(Operators.max, values);
assertEquals(results, Arrays.asList(1, 5, 5, 7, 7, 7));