Funções de Alta Ordem e Lambda

Em Python, funções também são "valores" — podem ser atribuídas a variáveis, passadas para outras funções, como números e strings. Isso permite código muito conciso e elegante. Funções de alta ordem são funções que recebem outras funções como argumentos ou retornam funções. Esta lição abrirá um novo mundo para você.


1. Funções são Cidadãos de Primeira Classe

Em Python, funções podem ser manipuladas como qualquer outro valor:

Exemplo: Funções como Valores

PYTHON
def say_hello(name):
    return f"Olá, {name}"

# Atribui uma função a uma variável
my_func = say_hello
print(my_func("Alice"))      # Olá, Alice

# Passa uma função como argumento
def greet_with(func, name):
    return func(name)

print(greet_with(say_hello, "Bob"))   # Olá, Bob

# Retorna uma função a partir de uma função
def create_greeter(greeting):
    def greeter(name):
        return f"{greeting}, {name}!"
    return greeter

say_hi = create_greeter("Oi")
say_hello = create_greeter("Olá")

print(say_hi("Alice"))       # Oi, Alice!
print(say_hello("Bob"))      # Olá, Bob!
▶ Experimente
💡 O que significa "funções são cidadãos de primeira classe"? Significa que funções estão no mesmo nível que inteiros, strings e listas — você pode atribuí-las a variáveis, colocá-las em listas, passá-las como argumentos e retorná-las como valores. Este paradigma de programação é chamado de "programação funcional."


2. map(): Transformação em Lote

map() aplica uma função a cada elemento de uma sequência e retorna um novo iterador:

Exemplo: map Transformação em Lote

PYTHON
# Elevar ao quadrado cada número em uma lista
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)              # [1, 4, 9, 16, 25]

# Converter lista de strings para maiúsculas
fruits = ["maçã", "banana", "cereja"]
upper_fruits = list(map(str.upper, fruits))
print(upper_fruits)         # ['MAÇÃ', 'BANANA', 'CEREJA']

# Celsius para Fahrenheit
temps_c = [0, 10, 20, 30, 40]
temps_f = list(map(lambda c: c * 9 / 5 + 32, temps_c))
print(temps_f)              # [32.0, 50.0, 68.0, 86.0, 104.0]
▶ Experimente

map() equivale a uma list comprehension: map(f, seq)[f(x) for x in seq]. Qual usar é questão de preferência — comprehensions são mais "pytônicas."


3. filter(): Filtragem em Lote

filter() seleciona elementos para os quais a função retorna True:

Exemplo: filter Dados

PYTHON
# Filtrar números pares
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)                # [2, 4, 6, 8, 10]

# Filtrar palavras com comprimento >= 5
words = ["cat", "elefante", "cachorro", "girafa", "formiga"]
long_words = list(filter(lambda w: len(w) >= 5, words))
print(long_words)           # ['elefante', 'cachorro', 'girafa', 'formiga']

# Filtrar strings não vazias
data = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, data))   # None significa "remover valores falsy"
print(non_empty)            # ['hello', 'world', 'python']
▶ Experimente

filter() também equivale a uma comprehension: filter(f, seq)[x for x in seq if f(x)]. Usar None como função equivale a remover todos os valores falsy ("", 0, None, False, etc.).


4. Uso Avançado de sorted()

A Lição 11 cobriu o parâmetro key de sorted(). Vamos nos aprofundar:

Exemplo: Ordenação Personalizada com sorted

PYTHON
# Ordenar por atributo de objeto
students = [
    {"name": "Alice", "score": 85},
    {"name": "Bob", "score": 92},
    {"name": "Charlie", "score": 78},
]

# Ordenar por pontuação decrescente
ranked = sorted(students, key=lambda s: s["score"], reverse=True)
for s in ranked:
    print(f"{s['name']}: {s['score']}")

# Ordenar por comprimento do nome
sorted_by_name = sorted(students, key=lambda s: len(s["name"]))
print([s["name"] for s in sorted_by_name])   # ['Bob', 'Alice', 'Charlie']
▶ Experimente

Exemplo: Ordenação Multi-Nível (Dificuldade ⭐⭐)

PYTHON
# Ordenar por pontuação decrescente, depois por nome alfabeticamente
data = [
    ("Alice", 85),
    ("Bob", 92),
    ("Charlie", 85),
    ("Diana", 92),
]

def sort_key(item):
    # Retorna tupla: primeiro por pontuação decrescente (negativo = reverso), depois por nome
    return (-item[1], item[0])

sorted_data = sorted(data, key=sort_key)
for name, score in sorted_data:
    print(f"{name}: {score}")
▶ Experimente

Saída:

TEXT
Bob: 92
Diana: 92
Alice: 85
Charlie: 85

5. reduce(): Computação Acumulativa

reduce() acumula elementos em uma sequência, aplicando uma função a dois elementos por vez. Não é embutido; importe de functools:

Exemplo: reduce Computação Acumulativa

PYTHON
from functools import reduce

# Produto de todos os números
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)              # 120 (1×2×3×4×5)

# Encontrar máximo
max_val = reduce(lambda a, b: a if a > b else b, [3, 1, 4, 1, 5])
print(max_val)              # 5

# Concatenar strings
words = ["Hello", " ", "World", " ", "Python"]
sentence = reduce(lambda a, b: a + b, words)
print(sentence)             # Hello World Python
▶ Experimente

Processo de Execução de reduce()

PYTHON
# reduce(lambda x, y: x + y, [1, 2, 3, 4])
# Passo 1: x=1, y=2 → 3
# Passo 2: x=3, y=3 → 6
# Passo 3: x=6, y=4 → 10
# Resultado: 10
💡 reduce() vs laços: Tudo que reduce() pode fazer, um laço for também pode. reduce() é mais conciso e declarativo — você está dizendo "o que fazer" em vez de "como fazer." A desvantagem é que pode ser mais difícil de entender para iniciantes.


6. Expressões Lambda em Profundidade

Lambda, nomeado após a letra grega λ, é essencialmente uma função anônima — uma pequena função sem nome, usada e descartada.

Lambda vs Funções Regulares

Exemplo: Lambda vs Regular

PYTHON
# Função regular
def double(x):
    return x * 2

# Expressão lambda
double = lambda x: x * 2

print(double(5))            # 10
▶ Experimente

Sintaxe Completa de Lambda

PYTHON
lambda parâmetros: expressão
# ↑ Equivalente a
def função_anônima(parâmetros):
    return expressão

Múltiplos Parâmetros

Exemplo: Lambda com Múltiplos Parâmetros

PYTHON
add = lambda a, b: a + b
print(add(3, 5))            # 8

# Expressão ternária em lambda
max_val = lambda a, b: a if a > b else b
print(max_val(10, 20))      # 20
▶ Experimente

Quando Usar Lambda

Exemplo: Casos de Uso de Lambda

PYTHON
# ✅ Bom: transformação simples de uma linha como argumento para função de alta ordem
numbers = [1, 2, 3, 4]
result = list(map(lambda x: x ** 2, numbers))

# ❌ Ruim: lógica complexa que requer múltiplas linhas — use uma função def regular
▶ Experimente
💡 Regra prática para lambda: Se a lógica precisa de mais de uma expressão ou ramificação if-elif, não use lambda — escreva uma função regular. A elegância do lambda está em seu "minimalismo."


Casos de Uso Comuns


❓ Perguntas Frequentes

P: Qual é melhor — map() ou list comprehensions? R: A comunidade Python prefere list comprehensions — mais intuitivas, mais pytônicas. map() tem vantagens: ① Mais curto quando você tem um nome de função existente (map(str.upper, seq) vs [s.upper() for s in seq]); ② Retorna um iterador preguiçoso, economizando memória para grandes dados. Na maioria dos casos, list comprehensions são a melhor escolha. ⚠️ P: Lambda pode abranger múltiplas linhas? R: Não. A sintaxe lambda é restrita a expressões únicas. Para lógica de múltiplas linhas, defina uma função regular. O lambda do Python é deliberadamente mais fraco que em outras linguagens (como JavaScript) — Guido (criador do Python) acredita que "explícito é melhor que implícito." P: reduce() ainda é usado no Python moderno? R: Não muito. O próprio Guido não gostava de reduce(), achando difícil de entender. A maioria dos cenários de reduce() pode ser substituída por um laço ou uma função mais direta (como sum(), max()). No entanto, reduce() continua sendo um conceito importante na programação funcional.


📖 Resumo


📝 Atividades

  1. Básico (Dificuldade ⭐): Dado nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], use map() e filter() para: filtrar números ímpares e calcular seus quadrados. Dica: Primeiro filter para ímpares, depois map para quadrados.

  2. Intermediário (Dificuldade ⭐⭐): Dada uma lista de dicionários products = [{"name": "A", "price": 100}, {"name": "B", "price": 80}, {"name": "C", "price": 120}], use sorted() para ordenar por preço decrescente e exiba nomes e preços.

  3. Desafio (Dificuldade ⭐⭐⭐): Use reduce() para implementar uma função flatten(nested_list) que achata uma lista aninhada em um nível. Por exemplo, flatten([[1, 2], [3, 4], [5]]) retorna [1, 2, 3, 4, 5]. Dica: Em reduce, use + para mesclar duas listas cada vez. Depois implemente o mesmo com uma list comprehension e compare as abordagens.

100%