Programação funcional trata computação como avaliação de funções. Ela evita estado mutável e efeitos colaterais no código. Primeiramente, funções são de primeira classe em Python. Por exemplo, você pode passar funções como argumentos. Além disso, funções podem retornar outras funções. A voz passiva é usada aqui: “os dados são transformados por funções puras”. Quando utilizar programação funcional? Em processamento de dados. Também em pipelines de transformação e análise. Python suporta functional com lambdas, map, filter e reduce. Vamos explorar os principais conceitos e práticas. Três subtítulos guiarão você pelo paradigma funcional. Ao final, você escreverá código mais declarativo e previsível.
Funções puras e imutabilidade
Função pura sempre retorna o mesmo resultado para mesma entrada. Ela não modifica variáveis externas nem tem efeitos colaterais. Quando usar funções puras? Em quase toda lógica de negócio. Elas são fáceis de testar e depurar. A imutabilidade evita bugs de estado compartilhado. A voz passiva é aplicada: “os dados originais não são alterados”. Exemplo de funções puras e impuras:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# Função pura (sem efeitos colaterais) def somar_puro(a, b): """Sempre retorna a+b. Não modifica nada externo.""" return a + b # Função impura (modifica estado global) contador = 0 def incrementar_impuro(): global contador contador += 1 return contador # Função impura (I/O) def ler_arquivo_impuro(caminho): with open(caminho, 'r') as f: return f.read() # Função pura com imutabilidade def processar_lista(lista_original): """Retorna nova lista sem modificar a original.""" return [x * 2 for x in lista_original] # Demonstração print("=== Funções Puras ===\n") print(f"somar_puro(3,5): {somar_puro(3,5)}") print(f"somar_puro(3,5): {somar_puro(3,5)} (sempre igual)") lista = [1, 2, 3, 4] nova_lista = processar_lista(lista) print(f"Lista original: {lista}") print(f"Nova lista: {nova_lista}") print(f"Original não foi modificada: {lista}") # Exemplo de função pura com dados imutáveis from copy import deepcopy def adicionar_usuario(usuarios, novo_usuario): """Retorna nova lista de usuários (imutável).""" # Cria cópia profunda para não modificar original nova_lista = deepcopy(usuarios) nova_lista.append(novo_usuario) return nova_lista usuarios = ["Ana", "Carlos"] novos = adicionar_usuario(usuarios, "João") print(f"\nUsuários original: {usuarios}") print(f"Usuários após adição: {novos}") |
Funções puras são a base da programação funcional. Elas tornam o código mais previsível e testável.
Map, filter e reduce: transformando dados
map aplica uma função a cada elemento de um iterável.
filter seleciona elementos que satisfazem uma condição.
reduce combina elementos em um único valor.
Quando usar essas funções? Em pipelines de processamento de dados.
Elas são mais declarativas que loops tradicionais.
A voz passiva é aplicada: “os dados são transformados em sequência”.
Exemplo de map, filter e reduce:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
from functools import reduce import math # Map: aplicar função a cada elemento numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # Versão funcional quadrados = list(map(lambda x: x ** 2, numeros)) print("=== Map ===") print(f"Original: {numeros}") print(f"Quadrados: {quadrados}") # Filter: selecionar elementos pares = list(filter(lambda x: x % 2 == 0, numeros)) print(f"\n=== Filter ===") print(f"Números pares: {pares}") # Reduce: combinar elementos soma = reduce(lambda a, b: a + b, numeros) produto = reduce(lambda a, b: a * b, numeros[1:5]) # 2*3*4*5 print(f"\n=== Reduce ===") print(f"Soma total: {soma}") print(f"Produto de 2 a 5: {produto}") # Pipeline funcional (encadeamento) resultado = reduce( lambda a, b: a + b, map( lambda x: x ** 2, filter(lambda x: x % 2 == 0, numeros) ) ) print(f"\n=== Pipeline ===") print(f"Soma dos quadrados dos pares: {resultado}") print(f"Verificação: 2²+4²+6²+8²+10² = {4+16+36+64+100}") # Exemplo prático: processamento de dados dados = [ {"nome": "Ana", "idade": 25, "salario": 3000}, {"nome": "Carlos", "idade": 35, "salario": 4500}, {"nome": "Beatriz", "idade": 28, "salario": 3200}, {"nome": "Daniel", "idade": 42, "salario": 5000}, {"nome": "Elena", "idade": 30, "salario": 3800}, ] # Salário médio de pessoas com mais de 30 anos salarios_acima_30 = list(map( lambda p: p["salario"], filter(lambda p: p["idade"] > 30, dados) )) media = reduce(lambda a, b: a + b, salarios_acima_30) / len(salarios_acima_30) print(f"\n=== Processamento de Dados ===") print(f"Salário médio acima de 30 anos: R${media:.2f}") # Usando map com múltiplos argumentos def potencia(base, expoente): return base ** expoente bases = [2, 3, 4, 5] expoentes = [10, 4, 3, 2] resultados = list(map(potencia, bases, expoentes)) print(f"\n=== Map com múltiplos argumentos ===") print(f"2^10, 3^4, 4^3, 5^2 = {resultados}") |
Map, filter e reduce substituem loops comuns. Eles expressam intenção de forma mais clara.
Lambda, closures e funções de alta ordem
Lambda são funções anônimas de uma única expressão. Closures capturam variáveis do escopo externo. Funções de alta ordem recebem ou retornam outras funções. Quando usar lambdas? Em operações simples e curtas. Para lógica complexa, prefira funções nomeadas. A voz passiva é aplicada: “as variáveis são capturadas pelo closure”. Exemplo de lambdas, closures e funções de alta ordem:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# Lambda: função anônima simples dobro = lambda x: x * 2 soma = lambda a, b: a + b impar = lambda x: x % 2 == 1 print("=== Lambda ===") print(f"dobro(5): {dobro(5)}") print(f"soma(10, 20): {soma(10, 20)}") print(f"impar(7): {impar(7)}") # Uso prático de lambda com sorted pessoas = [("Ana", 25), ("Carlos", 35), ("Beatriz", 28)] ordenado_por_idade = sorted(pessoas, key=lambda p: p[1]) print(f"\nOrdenado por idade: {ordenado_por_idade}") # Closures: função que lembra o ambiente def multiplicador(fator): """Retorna uma função que multiplica pelo fator.""" def multiplicar(x): return x * fator return multiplicar dobrar = multiplicador(2) triplicar = multiplicador(3) print(f"\n=== Closures ===") print(f"dobrar(10): {dobrar(10)}") print(f"triplicar(10): {triplicar(10)}") # Closure com estado (simulando objetos) def contador(): """Retorna funções para incrementar e obter valor.""" valor = 0 def incrementar(): nonlocal valor valor += 1 return valor def obter(): return valor return incrementar, obter inc, get = contador() print(f"inc(): {inc()}") print(f"inc(): {inc()}") print(f"inc(): {inc()}") print(f"get(): {get()}") # Função de alta ordem que retorna função def criar_operacao(operador): """Retorna função matemática baseada no operador.""" if operador == "+": return lambda a, b: a + b elif operador == "-": return lambda a, b: a - b elif operador == "*": return lambda a, b: a * b elif operador == "/": return lambda a, b: a / b else: raise ValueError("Operador inválido") somar = criar_operacao("+") multiplicar = criar_operacao("*") print(f"\n=== Função de Alta Ordem ===") print(f"somar(8, 3): {somar(8, 3)}") print(f"multiplicar(8, 3): {multiplicar(8, 3)}") # Composição de funções def compor(f, g): """Retorna a composição f ∘ g.""" return lambda x: f(g(x)) def mais_um(x): return x + 1 def vezes_dois(x): return x * 2 mais_um_depois_vezes_dois = compor(mais_um, vezes_dois) vezes_dois_depois_mais_um = compor(vezes_dois, mais_um) print(f"\n=== Composição de Funções ===") print(f"(f ∘ g)(5) = mais_um(vezes_dois(5)) = {mais_um_depois_vezes_dois(5)}") print(f"(g ∘ f)(5) = vezes_dois(mais_um(5)) = {vezes_dois_depois_mais_um(5)}") # Partial (fixar argumentos) from functools import partial def potencia(base, expoente): return base ** expoente quadrado = partial(potencia, expoente=2) cubo = partial(potencia, expoente=3) print(f"\n=== Partial ===") print(f"quadrado(5): {quadrado(5)}") print(f"cubo(5): {cubo(5)}") |
Lambda é útil para funções pequenas e descartáveis. Closures e funções de alta ordem permitem composição elegante. A fórmula da programação funcional pode ser expressa: \(F = \text{pureza} + \text{imutabilidade} + \text{composição}\) Programação funcional não é apenas sobre map/filter/reduce. É uma mentalidade de transformar dados, não modificá-los. Combine técnicas funcionais com OOP para o melhor dos mundos. Seu código será mais claro, testável e previsível.