Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 16: Programação funcional


Programação funcional é um paradigma que trata a computação como uma avaliação de funções matemáticas. Tais funções podem ser aplicadas em sequências de dados (geralmente listas). São exemplos de linguagens funcionais: LISP, Scheme e Haskell (esta última influenciou o projeto do Python de forma marcante).

As operações básicas do paradigma funcional são implementadas no Python pelas funções builtin map(), filter(), reduce() e zip().

Lambda

No Python, lambda é uma função anônima composta apenas por expressões. As funções lambda podem ter apenas uma linha, e podem ser atribuídas a uma variável. Funções lambda são muito usadas em programação funcional.

Sintaxe:

lambda <lista de variáveis>: <expressões>

Exemplo:

In [1]:
# Amplitude de um vetor 3D
amp = lambda x, y, z: (x ** 2 + y ** 2 + z ** 2) ** .5

print amp(1, 1, 1)
print amp(3, 4, 5)
1.73205080757
7.07106781187

Funções lambda consomem menos recursos computacionais que as funções convencionais, porém são mais limitados.

Mapeamento

O mapeamento consiste em aplicar uma função a todos os itens de uma sequência, gerando outra lista contendo os resultados e com o mesmo tamanho da lista inicial.

Exemplo de mapeamento

No Python, o mapeamento é implementado pela função map().

Exemplos:

In [2]:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

# log na base 10
from math import log10
print map(log10, nums)

# Dividindo por 3
print map(lambda x: x / 3, nums)
[0.0, 0.3010299956639812, 0.47712125471966244, 0.6020599913279624, 0.6989700043360189, 0.7781512503836436, 0.8450980400142568, 0.9030899869919435, 0.9542425094393249, 1.0, 1.041392685158225, 1.0791812460476249]
[0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4]

A função map() sempre retorna uma lista.

Filtragem

Na filtragem, uma função é aplicada em todos os itens de uma sequência, se a função retornar um valor que seja avaliado como verdadeiro, o item original fará parte da sequência resultante.

Exemplo de filtragem

No Python, a filtragem é implementada pela função filter().

Exemplo:

In [3]:
# Selecionando apenas os ímpares
print filter(lambda x: x % 2, nums)
[1, 3, 5, 7, 9, 11]

A função filter() aceita também funções lambda, além de funções convencionais.

Redução

Redução significa aplicar uma função que recebe dois parâmetros, nos dois primeiros elementos de uma sequência, aplicar novamente a função usando como parâmetros o resultado do primeiro par e o terceiro elemento, seguindo assim até o final da sequência. O resultado final da redução é apenas um elemento.

Exemplo de redução

Exemplos de redução, que é implementada no Python pela função reduce():

In [4]:
nums = range(100)

# Soma com reduce (pode concatenar strings)
print reduce(lambda x, y: x + y, nums)

# Soma mais simples, mas só para números
print sum(nums)
4950
4950

A função reduce() pode ser usada para calcular fatorial:

In [5]:
# Calcula o fatorial de n
def fat(n):
    return reduce(lambda x, y: x * y, range(1, n))

print fat(6)
120

A partir da versão 2.6, o módulo math traz uma função que calcula fatorial chamada factorial().

Transposição

Transposição é construir uma série de sequências a partir de outra série de sequências, aonde a primeira nova sequência contém o primeiro elemento de cada sequência original, a segunda nova sequência contém o segundo elemento de cada sequência original, até que alguma das sequências originais acabe.

Exemplo de transposição

Exemplo de transposição, que é implementada no Python pela função zip():

In [6]:
# Uma lista com ('a', 1), ('b', 2), ...
from string import ascii_lowercase
print zip(ascii_lowercase, range(1, 100))

# Transposta de uma matriz
matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print zip(*matriz)
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8), ('i', 9), ('j', 10), ('k', 11), ('l', 12), ('m', 13), ('n', 14), ('o', 15), ('p', 16), ('q', 17), ('r', 18), ('s', 19), ('t', 20), ('u', 21), ('v', 22), ('w', 23), ('x', 24), ('y', 25), ('z', 26)]
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

A função zip() sempre retorna uma lista de tuplas.

List Comprehension

Em computação, List Comprehension é uma construção que equivale a uma notação matemática do tipo:

$$ S = \\{ x^{2} \forall \in \mathbb{N}, x \geq 20 \\} $$

Ou seja, S é o conjunto formado por x ao quadrado para todo x no conjunto dos números naturais, se x for maior ou igual a 20.

Sintaxe:

lista = [ <expressão> for <referência> in <sequência> if <condição> ]

Exemplo:

In [7]:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

# Eleve os ímpares ao quadrado
print [ x**2 for x in nums if x % 2 ]
[1, 9, 25, 49, 81, 121]

List Comprehension é mais eficiente do que usar as funções map() e filter() tanto em termos de uso de processador quanto em consumo de memória.

Generator Expression

Generator Expression é uma expressão que se assemelha ao List Comprehension, porém funciona como um gerador.

Exemplo:

In [8]:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

# Eleve os ímpares ao quadrado
gen = ( x**2 for x in nums if x % 2 )

# Mostra os resultados
for num in gen:
    print num
1
9
25
49
81
121

Outro exemplo:

In [9]:
# Uma lista de tuplas (artista, faixa):
instrumentais = [('King Crimson', 'Fracture'),
    ('Metallica','Call of Ktulu'),
    ('Yes', 'Mood for a Day'),
    ('Pink Floyd', 'One of This Days'),
    ('Rush', 'YYZ')]

# Filtra e ordena apenas as faixas de artistas anteriores a letra N
print sorted(faixa[-1] + ' / ' + faixa[0]
    for faixa in instrumentais if
    faixa[0].upper() < 'N')
['Call of Ktulu / Metallica', 'Fracture / King Crimson']

Generator Expression usa menos recursos do que o List Comprehension equivalente, pois os itens são gerados um de cada vez, apenas quando necessário, economizando principalmente memória.

In [1]:
 
Out[1]: