程式扎記: [ 文章收集 ] List Comprehension in Groovy

標籤

2015年7月5日 星期日

[ 文章收集 ] List Comprehension in Groovy

Source From Here 
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: 
  1. 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: 
  1. L = [x**2 for x in range(111if x % 2 == 0]  
How to write this in Groovy? In Groovy, you can create list in a declarative way by specifying a range which is declared by using the double-point notation (..): 
  1. assert 1..10 == [12345678910]  
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. 
  1. assert (1..10).collect { it**2 } == [149162536496481100]  
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()
  1. L = (1..10).findAll({ it % 2 == 0 }).collect { it**2 }  
Or, in order to have a more explicit notation: 
  1. 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: 
  1. 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: 
  1. 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: 
  1. 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. (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.

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!