Preface
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 }
In Python, you will write this:
- L = [x**2 for x in range(1, 11) if x % 2 == 0]
- assert 1..10 == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- assert (1..10).collect { it**2 } == [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- L = (1..10).findAll({ it % 2 == 0 }).collect { it**2 }
- L = (1..10).findAll({ x -> x % 2 == 0 }).collect { x -> x**2 }
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) })
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))])
- assert (1..5).inject(0) { oldValue, element -> element + oldValue } == 15
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 }
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.
沒有留言:
張貼留言