Ranges in Apache Commons Functor

This is a long post. So here is a TL;DR:

Now, if you have some spare time or curiosity, keep reading :-)

This post is about ranges in Apache Commons Functor but first let me explain the context. I am working on nebular, an open source fuzzy logic API. This API is written in pure Java, and has Matlab Fuzzy Toolbox as reference for the initial release. Nebular will use functional programming to let the user to model his/her membership functions. The API chosen for functional programming is Apache Commons Functor.

Yesterday I finished to port sigmf function to nebular, including some tests that compare nebular’s output to Matlab sigmf function. However, here comes the issue with ranges, sequences and generators. The idea is to plot a graph similar to the one below.

The x axis contains values from 0 to 10, increasing by 0.1. In Matlab, I would execute the following command to produce the x vector.

x = 0:0.1:10;

The : is the range operator in Matlab. It would create a vector with 101 elements. Yup, 101. That’s because in Matlab the low and high value used creating the range are inclusive. Now let’s look at how I create similar vector in Apache Commons Functor.

IntegerRange range = new IntegerRange(0, 11, 1 /* step */);

So now one can realize two things: there is no Double or Float Range, and the high value in the range is exclusive. It means that in order to create an array (I will use vector and array interchangeably here for simplicity) with Functor from 0 to 10, including both values, you have to create a range using 0 and 11.

Comparing with other Java functional programming API's

There are other API's to include functional programming features to Java code. In this comparison I will include Google Guava, fun4jfunctionaljava, lambdaj and op4j (got the list from the fun4f project page).

Google Guava

Google Guava provides the Range and Ranges classes. The Ranges contains several static methods for creating Range's, that can be used as collections. The Ranges class contains methods for creating Range's with open, closed, openClosed and closedOpen intervals.
public static void main(String[] args) {
    Range<Integer> values = Ranges.closed(0, 10);
    System.out.print("[ ");
    for(Integer value : values.asSet(DiscreteDomains.integers())) {
        System.out.print(value + " ");
    }
    System.out.print("]");
}
[ 0 1 2 3 4 5 6 7 8 9 10 ]

The Google Guava API does not provide a way to create ranges of floats. And I will have to find some time to read about the asSet method and on discrete domains. I am much more comfortable putting Apache Commons Functor API in my code for creating ranges. Specially since it makes the code more readable and easy to understand, helping to receive contributions in nebular from newcomers.

fun4j

fun4j contains classes to model functions, lambdas and bind LISP code to Java. However it has no objects for ranges.

functionaljava

Although there is the Seq class, I don't know if that can be used for Ranges. So I will have to skip functionaljava :-(

lambdaj

Couldn't find a way to create ranges with lambdaj. Not sure if they support it or not.

op4j

Same as lambdaj, couldn't find a way to create ranges with op4j. Not sure if they support it or not.

Comparing with other programming languages

So let's see how we could generate a similar array using other programming languages.

Python

Python is one of my favorite languages, and has some functional programming features [1]. However it has no built-in mechanism for ranges with floats, only with integers. The functions that I know in Python use low value as inclusive, and high value as exclusive. The built in range functions in 2.7 looks like the following.
x = range(0, 11, 1) #inclusive/exclusive
print x;
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Or you could write something like (remember Perl’s motto, TIMTOWTDI [2])

x = [x * 0.1 for x in range(0, 101)]; #inclusive/exclusive

print len(x);
print x;
101
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, ..., 10.0]

There are API’s in Python too, like numpy, that has arange. However it is complicated specify a range to give similar array as the one produced by Matlab.

from numpy import arange;
x = arange(0, 10.1, .1); #inclusive/exclusive
print len(x);
print x;
101
[ 0. 0.1 0.2 ... 10.0]

Perl

As we mentioned Perl in last section, let's see how it works in Perl. First of all, I won't use many modules here. Probably Perl is the language with more modules, or at least with a plethora of modules in CPAN (wait to see CJAN ;). There are books on Perl and Functional programming [3], and Perl has a built in mechanism for ranges. I used Perl 5 built in range operator and List::Gen range function. Both use the low and high value inclusive. It's important to point out that Larry Wall and the programmers behind Perl are always keeping an eye on usability, ease of use, and Larry's laziness. So that's why Perl is a good language for comparisons of this type.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';

print "[ ";
for(0..10) { #inclusive/inclusive
    print $_, " ";
}
print " ]"
[ 0 1 2 3 4 5 6 7 8 9 10 ]

Or to use floats, we could use List::Gen module.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';
use List::Gen;

my $range = range 0, 10, 0.1; #inclusive/inclusive

print "[ ";
print $_, " " for @$range;
print " ]"
[ 0 0.1 0.2 0.3 0.4 ... 10 ]

Perl also has support to range of characters. Look at the examples from Perldoc.

@alphabet = ("A" .. "Z"); # inclusive/inclusive... how would it work if the last argument was not inclusive?

That would be cool if Functor had this feature too ;-)

Scala

Scala is a language that runs on the Java Virtual Machine, it has some functional programming features as anonymous functions, higher order functions, among others. It has a built in mechanism for ranges with integers or floats. It uses the low value as inclusive, and the high value as exclusive, however when using floats you may have trouble if you don't pay attention to float precision.
object Test {
    def main(args: Array[String]) {
        var range = 0.0 until 10.0 by 0.1;
        println(range);
    }
}
NumericRange(0.0, 0.1, 0.2, ..., 9.99999999999998)

Matlab

Why, yes sir, we will compare Matlab too. It has built in operator for ranges, and both values, low and high, are inclusive.
x=0:0.1:10;
display(x);
x =
Columns 1 through 9
0 0.1000 ...
... 10.0000

PHP

Although famous for its web applications, PHP can be an interesting language for scripting or creating handy utilities. I may be wrong, but I believe the scm_bot in Jenkins project, that posts messages to JIRA when someone commits to a SCM with a special string, is written in PHP. It has a built in function for ranges in 5.3, and both values are inclusive.

$range = range(0, 10, 0.1);
echo "[ ";
foreach($range as $number) {
    echo $number . " ";
}
echo "]";
[ 0 0.1 0.2 0.3 ... 9.9 10 ]

Haskell

Haskell has gained more attention lately with web frameworks. It has features such as monads, functors and high order function. And also has a built-in range operator. Both, left and right values of the range are inclusive in Haskell's range operator.
[0 .. 10]
[0,1,2,3,4,5,6,7,8,9,10]

Unfortunately I don’t know Haskell so well, and couldn’t make it work with floats, nor find out whether it is possible or not :-(

lua

While lua has no built-in support to ranges, if you search for lua and range functions or operators, probably you will find an entry in lua's Wiki with a range function, contributed by a user. The function implemented uses the low and high value inclusively and has support for both integer and floats.
-- http://lua-users.org/wiki/RangeIterator
function range(from, to, step)
    step = step or 1
    return function(_, lastvalue)
    local nextvalue = lastvalue + step
    if step > 0 and nextvalue <= to or step < 0 and nextvalue >= to or
        step == 0
    then
        return nextvalue
    end
    end, nil, from - step
end

function f() return 0, 10, 0.1 end

io.write("[ ");
for i in range(f()) do
    io.write(i, " ")
end
io.write("]");
[ 0 0.1 0.2 0.3 ...  9.9 10 ]

LISP

Lisp is probably one of the oldest programming languages with functional features. Every time I read something about functional programming and collections it reminds me of Lisp recursion with lists. In Lisp you can use loop to iterate over a range of integers or floats, and both values are included for integers, for float, there is some precision problem that I couldn't overcome. For integers the following code would do.
>(loop for i from 0 to 10 do (print i))
0
1
2
...
10
NIL

And for floats.

>(loop for i from 0.0 to 10.0 by 0.1 do (print i))
0.0
0.1
0.2
...
9.900002
NIL

Again, issues with precision.

Clojure

Although Clojure is quite similar to LISP, I had some trouble setting up an example for this. My first try was with loop, but as it didn't work, I used dorun. It uses the low value as inclusive, and the high value as exclusive. Strange, I thought it would have similar behavior to the loop in LISP. But I only know the very basics of each language, and the explanation is beyond my knowledge. Anyway, it's interesting I guess.
(dorun (for [i (range 0 10 1)] (println i)))
0
1
...
9
nil

Comparison Table

To simplify the understanding, here is a comparison table.
 

Apache Commons Functor

Google Guava

fun4j

functionaljava

lambdaj

op4j

Python

Python – numpy.arange

Perl

Perl List::Gen

Scala

Matlab

PHP

Haskell

lua (Wiki contrib)

LISP

Clojure

Integer range

YES

YES

N/A

N/A

N/A

N/A

YES

YES

YES

YES

YES

YES

YES

YES

YES

YES

YES

Float range

NO

NO

N/A

N/A

N/A

N/A

NO

YES

NO

YES

YES

YES

YES

NO?

YES

YES

NO?

Includes low limit

YES

YES/NO

N/A

N/A

N/A

N/A

YES

YES

YES

YES

YES

YES

YES

YES

YES

YES

YES

Includes high limit

NO

YES/NO

N/A

N/A

N/A

N/A

NO

NO

YES

YES

NO

YES

YES

YES

YES

YES?

NO


That's it, I will ping some guys from Apache dev-list to see what they think about the bullets in TL;DR. After writing this post I realized I need to change this blog's layout to give more space for code and tables :-(

Cheers

Categories: Blog

Tags: Apache Software Foundation, Functional Programming