Operadores Map e Reduce em Groovy

Veja neste artigo como aplicar os operadores map e reduce da programação funcional utilizando a linguagem Groovy.


Imperativo vs Declarativo

Atualmente, o desenvolvimento de software é massivamente feito mediante o paradigma imperativo, especificamente utilizamos a orientação a objetos para estruturar nosso código.

Nele, modelamos um conjunto de entidades, físicas e/ou abstratas, e definimos o comportamento de cada uma.

Elementar a este processo, esta o fato de que “ordenamos” ao processador que execute “este, este e aquele comando”, que execute “este loop enquanto a condição for satisfeita” etc. Resumidamente, imperamos como fazer.

Outra forma de programar é mediante ao paradigma declarativo, neste paradigma, somente indicamos o quê fazer, não nos preocupando em como fazer, exemplo disso são as queries em SQL que costumamos escrever corriqueiramente.

Um proeminente membro do paradigma declarativo é a programação funcional. Ela tem sua base construída em conceitos abstratos da Matemática, especialmente Cálculo Lambda.

Quando utilizamos programação funcional na escrita de nosso código, nós criamos um conjunto de unidades elementares denominadas funções.

Funções aceitam parâmetros de entrada, processam e produzem determinado resultado. Um ponto de destaque, e polêmica, é que funções não devem produzir efeitos colateiras, basicamente não devem alterar os parâmetros passados. Exemplo, uma função que ordena elementos de um vetor não devem manipular o vetor fornecido, mas sim criar um novo, ordená-lo e retornar este para quem invocou a função.

Existem diversos conceitos relacionados à programação funcional e escrever código puramente funcional é um desafio que requer certo esforço. Entretanto, dois de seus conceitos que podemos destacar e utilizar de forma razoavelmente simples em nossa rotina diária são os operadores map e reduce.

Apesar de serem muito utilizados na programação funcional, map e reduce podem ser aplicados no paradigma imperativo, seja orientação a objetos, programação estruturada e com certa paciência até mesmo em Assembly!


Map

O operador map tem como função mapear valores de determinado domínio, para valores pertencentes a outro domínio. Exemplos: mapear nomes de ruas para seus respectivos CEPs, mapear uma coleção de pessoas para uma coleção de seus IDs etc.

Em Groovy, podemos utilizar o map de diversas maneiras, uma delas é escrever a própria implementação como abaixo:

Esta função aceita a coleção, bem como uma outra função (na verdade uma Closure, mas isto fica para outra publicação) como parâmetros. Para cada elemento da coleção, é chamada a função para efetuar sua transformação. Ao final, retornamos uma nova coleção, sem alteração na coleção fornecida, sem efeitos colaterais.

Como exemplo de aplicação, podemos utilizar essa função para elevar os elementos de uma lista ao quadrado:

Interessante notar que a função map é desacoplada de seu uso, podemos facilmente escrever código para utilizar a função de outras maneiras além do exemplo fornecido.

Groovy já fornece uma implementação em sua GDK com o método collect. Para o mesmo exemplo:

E o resultado é o mesmo.


Reduce

Enquanto o map efetua o mapeamento de coleções, o reduce é um tanto mais sútil, ele recebe uma coleção e retorna um único valor a partir desta coleção. Exemplos: soma de uma série de números, ID da pessoa mais velha em uma coleção de pessoas etc.

Semelhante ao map, podemos implementar nossa própria função reduce, em Groovy:

Nela, recebemos o valor inicial do resultado, a coleção e a função (novamente, na verdade uma Closure) responsável por manipular a coleção e retornar um valor a partir desta. A cada iteração, aplicamos a função no resultado parcial e no elemento corrente da coleção e igualamos seu retorno ao resultado corrente.

Como exemplo de uso, podemos ter uma função que tome o produto de números de uma coleção:

Novamente, nota-se que a função reduce é desacoplada de seu uso.

Assim como map, Groovy fornece uma implementação do reduce em sua GDK, este método é denominada inject. Para o mesmo exemplo:

E o resultado é o mesmo.


Conclusão

Neste artigo foi fornecida uma pequena introdução a dois conceitos do paradigma funcional utilizando a linguagem Groovy. Ressalta-se que o tema é vasto, mas seu aprendizado aumenta seu arsenal de ferramentas e pode melhorar a qualidade de seu código.