Kerflyn's Blog

Well… It's a blog!

Posts Tagged ‘list comprehension

List Comprehension in Groovy

At present, I am discovering the language Groovy. It is a flexible language. You can choose between dynamic or static type. It provides closures. And above all, it produces Java bytecode after a compilation. These points make Groovy an exciting language.

For me, Groovy is a language that stands between Python and Java. And I really like these two languages. But I was a bit disappointed because there is no explicit syntax to express list comprehension. What a shame! This is really useful because you can create, for example, many kind of lists in one line in a intuitive way… In fact, I was wrong.

Here, we will see how to express the list comprehension in Groovy. We will generate lists in one line of code and even mappings.

List comprehension and Groovy

A list comprehension is syntactic construct that enables you to built lists (or even all kind of collections) in a declarative way. That is to say that you do not have to put values one by one in the list by hand or with the help of a loop and a set of instructions. In fact, the values are automatically computed through a generic description you have provided. For example, you want L be the set of all square integers, for even integers between 1 and 10. This is the mathematical way of expressing it:

L = {  x² | x ∈ {1, …, 10} ∧ x mod 2 = 0 }

Thus, L is the set {4, 16, 36, 64, 100}.

In Python, you will write this:

L = [x**2 for x in range(1, 11) if x % 2 == 0]

But how to write this in Groovy? In Groovy, you can create list in a declarative way by specifying a range. A range is declared by using the double-point notation (..):

assert 1..10 == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

The method collect() can apply a closure on each element of a list in order to built a new list that contains the results of the closure.

assert (1..10).collect { it**2 } == [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Now, we have to reduce this list in order to meet the definition of L, describes above. To apply a filter on the list elements, I use the method findAll().

L = (1..10).findAll({ it % 2 == 0 }).collect { it**2 }

Or, in order to have a more explicit notation:

L = (1..10).findAll({ x -> x % 2 == 0  }).collect { x -> x**2 }

Thus, at the end, L is equal to [4, 16, 36, 64, 100].

Now suppose that you have the function randomString(n) that generate strings of size n with random content. You want to generate a list with 10 random strings of size 5:

println((1..10).collect { randomString(5) })

Convert a map into a list

Let’s go beyond! Suppose we have a set of characters: C = { ‘A’, …, ‘J’ }. You want to produce a map between each character of C and its number in the alphabet. Thus, you to have M = { ‘A’: 1, ‘B’:2, …, ‘J’: 10 }.

In Python, you write something like that:

M = dict([(C[i], i) for i in range(len(C))])

In Groovy, we need the help of the method inject(). inject() is a method that takes two arguments: an initial value and a closure. inject() iterates through the element of a collection. these elements are used by the closure in order to produce a new value from the preceding one, starting with the initial value. For example, with an initial value to 0 and a closure that adds two elements, the result of inject() is the sum of all elements in the collection:

assert (1..5).inject(0) { oldValue, element -> element + oldValue } == 15

With an initial value to 1 and a closure that multiplies two elements, the result is the product of all the element of the collection (it is also a Groovy way to have a simple implementation of the factorial function).

Now here is the answer to our initial problem. Let C be declared as def C = 'A'..'J'. The idea is to start with an empty map and to add an entry in this map for each visited element.

(1..C.size()).inject([:]) { m, i -> m[C[i-1]] = i; m }

The result is [A:1, B:2, C:3, D:4, E:5, F:6, G:7, H:8, I:9, J:10].

Conclusion

The bad news are that there is no syntactic sugar in Groovy in order to implement the list comprehension. But I do not say that you cannot implement such a structure. Indeed, we have seen that with methods like findAll(), collect(), or inject() and with the help of closures, we are able to express the list comprehension without adding extra language features.

Advertisement

Written by fsarradin

2010/06/25 at 12:51

Posted in Programming

Tagged with ,