March 2, 2008
I’ve added Python-style list comprehensions to javac
. It works on
Iterables
. You can do things like:
Iterable<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
list = [x*x for int x : list if x >= 5];
You’ll get an Iterable
that can generate the sequence 25, 36, 49, 64, and 81.
I use comprehensions all the time in Python to process text files. In Java this
would look like:
Iterable<String> lines = new ScannerIterable(new File("input.txt"));
// Remove end-of-line character.
lines = [line.substring(0, line.length() - 1) for String line : lines];
// Strip leading and trailing whitespace.
lines = [line.trim() for String line : lines];
// Remove blank lines.
lines = [line for String line : lines if !line.isEmpty()];
// Remove comments.
lines = [line for String line : lines if !line.startsWith("#")];
// Split up at spaces.
Iterable<String[]> tokenizedLines = [line.split(" ") for String line : lines];
(ScannerIterable
is a helper class that generates a new Scanner
each time
iterator()
is called, setting the delimiter to end-of-line.)
I find this way of coding very readable and maintainable, plus it doesn’t load
the whole file into memory. The motivation for this came from all the recent
discussions about closures. It occurred to me that although I never used
map()
and filter()
in Python, once comprehensions were introduced in Python
2.0 I found myself using them pervasively. The map+lambda (or filter+lambda)
syntax never clicked with me, but the comprehension syntax came naturally.
Comprehensions don’t replace closures, of course, but they may provide a more
natural expression when mapping and filtering.
A binary
release
(broken link—contact me if you want it) is available for testing, with
examples. The source is also available; it’s the listcomprehensions
branch of
the kijaro project. Look for LISTCOMP
comments for the changes, or diff with
the trunk. You can also read a more thorough write-up of the motivation and
spec, with more
examples.