A programação funcional é um paradigma que se baseia inteiramente no uso de funções, ou seja, você pensa em programação funcional como simplesmente avaliações de funções, onde uma função pode ser complementada com outra(s) função(ões).
Mas qual a vantagem disso?
Bom, geralmente linguagens que implementam o paradigma funcional possuem algumas funções/recursos especialmente para esse paradigma, que visa tornar os programas mais ágeis, curtos, concisos, e menos suscetíveis a erros de estruturação.
Existem linguagens puramente funcionais (como é o caso de Lisp, que tem seu uso voltado 95% para inteligência artificial) e linguagens multiparadigmas, que é o caso de Python (onde você pode programar no paradigma procedural, orientado a objetos, funcional...).
Vamos então ver as estruturas que nos permitem trabalhar funcionalmente com Python:
sum
A mais básica. Ela recebe uma lista e devolve a soma e todos os seus valores
sum( range(11) ) # somando todos os números até 10
Resultado seria 55.
lambda
Funções lambda são funções anônimas, ou seja, não são reutilizáveis (na verdade podem ser sim, mas não foram feitas pra isso e você já vai entender o por que em alguns "episódios").
Daí você se pergunta:
"Qual a utilidade de uma função se ela não pode ser reutilizada?"
Daí eu te falo, tenta imaginar alguma relação entre lambdas e processamento de listas (sacô sacô?).
Para criar uma lambda fazemos assim:
lambda <argumentos> : <expressão>
Onde <argumentos> é a lista de argumentos que serão passados para a função (dificilmente você precisará usar mais do que 1) e <expressão> é uma expressão avaliando os argumentos e retornando seu valor.
Você pode atribuir uma lambda a uma variável e então reutilizá-la (esse pode ser um jeito curto de escrever funções curtas, mas o objetivo de lambda não é esse).
f = lambda x: x*2
f(2) # retorna 4
Agora sim, no próximo parágrafo vamos ver a real utilidade de lambdas...
map
Map é uma função que "mapeia" uma lista em uma função, e seu uso é:
map(<função>, <lista>)
Explicando, map retorna uma lista com os resultados de cada valor da lista original submetido a função, vamos ver um exemplo prático disso (mão na cabeça que vai começaaar.).
lista = range(1, 11) # lista de 1 a 10.. sim,de 1 a 10, o 11 é exclusivo
nova_lista = map(lambda x:x*2, lista)
for i in nova_lista:
print i
A saída seria:
2
4
6
8
10
12
14
16
18
20
Perceberam como lambda é útil? Essa é uma aplicação simples, mas imagine algo mais complexo (programação funcional aplicada pode ser muito complexa).
filter
Exatamente, é um filtro. Ele funciona quase igual ao map, retorna uma lista, mas a diferença é que a função que ele aplica aos elementos da lista é uma função que retorna verdadeiro ou falso, e ele só retorna na nova lista os elementos que passarem pela função e retornarem verdadeiro.
Vamos ver um exemplo de uma função que só retorna números pares:
lista = range(1, 11)
nova_lista = filter(lambda x: x % 2 == 0, lista)
for i in nova_lista:
print i
Saída:
2
4
6
8
10
Bem fácil de sacar né? :D
reduce
Bom, pode parecer complicada mas é bem fácil.
É o seguinte, nas funções acima nós usamos lambdas de apenas 1 argumento certo? Como reduce é uma daquelas exceções onde você vai usar 2. Ela funciona aplicando os argumentos de 2 em 2, e é acumulativa, ou seja, numa lambda;
lambda x,y: x + y
E suponhamos que a lista seja de 1 a 10, o reduce faria:
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15
15 + 6 = 21
21 + 7 = 28
28 + 8 = 36
36 + 9 = 45
45 + 10 = 55
Entenderam? A saída acima seria gerada pela seguinte reduce:
lista = range(1, 11)
nova_lista = reduce(lambda x,y: x+y, lista)
for i in nova_lista:
print i
Sacaram né? Bem molezinha e útil. :D
zip
Nossa última "arma" funcional, e não, ela não compacta arquivos. ;)
O que a função zip faz é receber várias listas e retornar uma lista contendo N tuplas (iguais as listas, mas são imutáveis), onde N é o número de elementos da maior lista fornecida.
Essas tuplas são agrupamentos de elementos de mesmo índice, de todas as listas, e caso as listas sejam de tamanhos diferentes, os valores que estiverem faltando são preenchidos com "None".
Vamos ver:
list1 = range(1, 6) # de 1 a 5
list2 = range(11, 16) # de 11 a 15
tuplas = zip(list1, list2)
print tuplas
A saída do programa seria:
[(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]
Reparem que cada tupla é um agrupamento de elementos de índices iguais(primeira tuplas contem os elementos de índices 0, a segunda de índices 1 etc).
Essa eu particularmente gosto de usar para transformar linhas em colunas, e colunas em linhas, em matrizes não muito grandes.
No projeto Euler, o primeiro problema proposto foi calcular a soma de todos os números abaixo de 1000 que fossem divisíveis tanto por 3 quanto por 5(não apenas por um, mas por ambos).. É fácil, você faria isso em poucas linhas.
Programando funcionalmente em Python, você faria em uma:
sum(filter(lambda x: not(x % 5 and x % 3), range(1000)))
Bom, é isso aí pessoal, espero que tenham gostado.
Té mais \õ/