O paradigma funcional é um paradigma que trata a computação como uma sequência de funções e não como uma sequência de acções que mudam o estado do programa. Neste paradigma não há dados mutáveis, tudo é constante: se x
é definido como sendo 3
, x
nunca vai poder ser 4
, 5
ou 6
. As variáveis são apenas nomes para valores (vulgo dados), e não uma caixa para o que lá quisermos colocar dentro, seja uma bola azul ou amarela. Esta abordagem é o que diferencia o paradigma funcional do imperativo: no paradigma funcional “transformam-se” valores, aplicam-se em novas situações, no paradigma imperativo alteram-se estados (o conteúdo das variáveis por exemplo).
O Python é uma linguagem que, no meu ponto de vista, extremamente flexível, adaptando-se facilmente às nossas necessidades, e que suporta 3 paradigmas: o funcional, o imperativo e o orientado a objectos. É dada uma grande ênfase à programação orientada a objectos (uma vez que tudo em Python são objectos e a própria linguagem “obriga” a perceber a lógica da programação orientada a objectos), mas não dá tanta à programação funcional. Neste artigo pretendo apenas elucidar o leitor sobre alguns dos recursos disponibilizados pela linguagem para a programação no paradigma funcional e o seu funcionamento, estando a explicação/análise da programação funcional em si fora do objectivo deste artigo.
Caso esteja interessado em ler sobre a programação funcional em si, devo referir que o artigo da Wikipedia em inglês (http://en.wikipedia.org/wiki/Functional_programming) está bastante completo e é um bom ponto de partida.
Funções anónimas (lambda)
No Python, à semelhança de outras linguagens, existem funções anónimas. A vantagem das funções anónimas sobre as funções normais (as definidas com o def
statement) em Python (e em todas as outras linguagens que suportam funções anónimas) é a possibilidade de definir rotinas sem as prender a um nome, são rotinas que não têm identidade, são abstractas, e podem ser usadas em qualquer lado. Para as funções descritas a seguir, vou usar funções anónimas, mas poderia usar igualmente funções normais. Se não conhece as funções anónimas em Python, os seguintes blocos de código são equivalentes (o primeiro usa funções anónimas, o segundo as funções ditas normais):
transformar = lambda x, y: x**y transformar(3, 4) #isto retorna 81
def transformar(x, y): return x**y transformar(3, 4) #isto também retorna 81
A vantagem do primeiro bloco de código sobre o segundo é o facto da função não estar presa àquele nome, ou seja, de a poder “destruir” quando quiser ou deixar de precisar dela, enquanto que com a definição tradicional não o consegue fazer.
Um outro exemplo:
iguais = lambda x, y: "Sim" if x == y else "Nao" iguais(54, 55) #isto retorna "Nao"
def iguais(x, y): if x == y: return "Sim" else: return "Nao" iguais(54, 55) #isto também retorna "Nao"
Sintaxe: lambda <argumentos separados por vírgulas> : <o que a função deve retornar>
Map
O map()
é uma função que recebe um ou mais objectos iteráveis, e itera os objectos aplicando-lhe uma função definida pelo programador, retornando uma lista com os elementos modificados.
Exemplo: map(lambda x: x**2, [1, 2, 3, 4, 5])
O map
neste caso vai retornar uma lista com todos os elementos da lista inicial elevados ao quadrado ([1, 4, 9, 16, 25]
).
Podemos passar mais do que um objecto iterável, no entanto, a função que é passada ao map
tem de receber tantos argumentos tantos os objectos iteráveis passados ao map
.
Exemplo: map(lambda x, y: [x**2, str(y)], [1, 2, 3, 4, 5], [3.5, 7, 5.1, 8, ‘a’])
Neste caso, o map
retornaria [[1, ‘3.5'], [4, ‘7'], [9, ‘5.1'], [16, ‘8'], [25, ‘a’]]
.
Uma situação que por vezes acontece é passarmos objectos iteráveis com um número diferente de elementos. Quando o map
tenta aceder ao quinto elemento de 2 objectos, e um desses objectos não o possui, é passado à função de controlo (a função que passamos ao map
) um None
.
Exemplo: map(lambda x, y, z: [x**2, str(y), z], [1, 2, 3, 4, 5], [3.5, 7, 5.1, 8, ‘a’], [True, False])
O retorno do map
neste caso seria [[1, ‘3.5', True], [4, ‘7', False], [9, ‘5.1', None], [16, ‘8', None], [25, ‘a’, None]]
.
Situações onde é possível usar o map, e as respectivas alternativas
Quando necessitamos de converter várias strings numéricas para inteiros:
map(lambda x: int(x), lista) #sendo lista uma lista de strings numéricas - > ['1', '3']
Uma alternativa seria o uso de listas por compreensão:
[int(x) for x in lista]
Ou então o uso do ciclo for
:
novaLista = [] for x in lista: novaLista.append(int(x))