🧩 Exercícios: Paradigma Funcional e Imutabilidade
Módulo 10: Paradigmas & Padrões • Bloco 1 • Trilha em 4 Níveis
🎯 Nível 1: Fundamentos
Problema 03.1 — Pureza e Eliminação de Efeitos Colaterais
Contexto: Funções impuras causam bugs imprevisíveis em sistemas paralelos ao mutar variáveis globais ou coleções externas.
Requisitos de Execução:
- Receba uma tupla imutável de pontuações de alunos:
(4.5, 8.0, 6.5, 9.2, 3.0, 7.5). - Escreva uma função pura
bonificar_aprovados_puro(notas: tuple, nota_corte: float, bonus: float) -> tuple. - A função deve filtrar notas $\ge \text{nota_corte}$, somar o bônus limitando a nota máxima a $10.0$, sem modificar a tupla original e sem usar variáveis fora de seu escopo.
Resultado Esperado
Tupla Original (Imutável): (4.5, 8.0, 6.5, 9.2, 3.0, 7.5)
Notas Aprovadas com Bônus: (9.0, 7.5, 10.0, 8.5)
Integridade da Entrada: Preservada com sucesso!
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_03_1_SeuNome.py - Envie na tarefa:
Atividade Cap 03 - Paradigma Funcional e Imutabilidade
🔑 Gabarito de Código & Solução Comentada
# Atividade_03_1_Gabarito.py
from typing import Tuple
def bonificar_aprovados_puro(notas: Tuple[float, ...], nota_corte: float = 6.0, bonus: float = 1.0) -> Tuple[float, ...]:
"""Função estritamente pura: sem efeitos colaterais e com transparência referencial."""
aprovados = filter(lambda n: n >= nota_corte, notas)
bonificados = map(lambda n: min(10.0, n + bonus), aprovados)
return tuple(bonificados)
if __name__ == "__main__":
notas_raw = (4.5, 8.0, 6.5, 9.2, 3.0, 7.5)
resultado = bonificar_aprovados_puro(notas_raw, 6.0, 1.0)
print(f"Tupla Original (Imutável): {notas_raw}")
print(f"Notas Aprovadas com Bônus: {resultado}")
assert notas_raw == (4.5, 8.0, 6.5, 9.2, 3.0, 7.5)
print("Integridade da Entrada: Preservada com sucesso!")
🔍 Nível 2: Prática
Problema 03.2 — Currying e Aplicação Parcial
Contexto: O Currying transforma uma função de múltiplos argumentos $f(a, b, c)$ em uma cadeia de funções unárias $f(a)(b)(c)$.
Requisitos de Execução:
- Crie uma função curried para cálculo de juros compostos:
calcular_montante(taxa)(periodos)(capital). - Utilize a aplicação parcial para criar duas calculadoras especializadas:
calculadora_renda_fixa_anual(Taxa fixa de $12\%$ ao ano);calculadora_curto_prazo_mensal(Taxa fixa de $1\%$ ao mês durante 6 períodos).
Resultado Esperado
[Renda Fixa 12% a.a.]
Investimento R$ 10000.00 por 3 anos: R$ 14049.28
[Curto Prazo 1% a.m.]
Investimento R$ 5000.00 por 6 meses: R$ 5307.60
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_03_2_SeuNome.py - Envie na tarefa:
Atividade Cap 03 - Paradigma Funcional e Imutabilidade
🔑 Gabarito de Código & Solução Comentada
# Atividade_03_2_Gabarito.py
from typing import Callable
def calcular_montante_curried(taxa: float) -> Callable[[int], Callable[[float], float]]:
"""Currying manual de três níveis para cálculo financeiro."""
return lambda periodos: lambda capital: round(capital * ((1 + taxa) ** periodos), 2)
if __name__ == "__main__":
# Aplicação Parcial de 1º e 2º nível
renda_fixa_anual = calcular_montante_curried(0.12)
curto_prazo_6m = calcular_montante_curried(0.01)(6)
m1 = renda_fixa_anual(3)(10000.0)
print("[Renda Fixa 12% a.a.]")
print(f"Investimento R$ 10000.00 por 3 anos: R$ {m1:.2f}")
m2 = curto_prazo_6m(5000.0)
print("[Curto Prazo 1% a.m.]")
print(f"Investimento R$ 5000.00 por 6 meses: R$ {m2:.2f}")
🚀 Nível 3: Integração
Problema 03.3 — Operador Pipe e Composição Pura de Pipelines
Contexto: Composição funcional conecta a saída de uma função diretamente como entrada da próxima sem variáveis intermediárias mutáveis.
Requisitos de Execução:
- Crie a função de alta ordem
pipe(*funcoes)que combine transformações da esquerda para a direita. - Construa um pipeline funcional para higienização e validação de documentos fiscais brutos:
remover_pontuacao: Limpa pontos, traços e barras;validar_tamanho: Garante 11 dígitos para CPF ou 14 para CNPJ;formatar_canonicamente: Adiciona tag do sistema[DOC_VALIDO].
Resultado Esperado
Entrada Bruta: ' 44.123.456/0001-99 '
Pipeline Executado com Sucesso: '[DOC_VALIDO:CNPJ] 44123456000199'
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_03_3_SeuNome.py - Envie na tarefa:
Atividade Cap 03 - Paradigma Funcional e Imutabilidade
🔑 Gabarito de Código & Solução Comentada
# Atividade_03_3_Gabarito.py
from functools import reduce
from typing import Callable, Any
import re
def pipe(*funcoes: Callable[[Any], Any]) -> Callable[[Any], Any]:
return lambda dado_inicial: reduce(lambda acc, f: f(acc), funcoes, dado_inicial)
def limpar_caracteres(texto: str) -> str:
return re.sub(r'[^0-9]', '', texto)
def validar_e_tipar(doc_limpo: str) -> str:
if len(doc_limpo) == 11:
return f"CPF:{doc_limpo}"
elif len(doc_limpo) == 14:
return f"CNPJ:{doc_limpo}"
raise ValueError(f"Documento com tamanho inválido: {len(doc_limpo)}")
def formatar_saida(tipo_doc: str) -> str:
tipo, num = tipo_doc.split(":")
return f"[DOC_VALIDO:{tipo}] {num}"
if __name__ == "__main__":
doc_raw = " 44.123.456/0001-99 "
processador = pipe(
limpar_caracteres,
validar_e_tipar,
formatar_saida
)
resultado = processador(doc_raw)
print(f"Entrada Bruta: '{doc_raw}'")
print(f"Pipeline Executado com Sucesso: '{resultado}'")
🏆 Nível 4: Desafio
Problema 03.4 — Implementação Monádica: Option/Maybe para Eliminação de Ponteiros Nulos
Contexto: O erro do “bilhão de dólares” (NullPointerException) é resolvido no paradigma funcional pelo tipo monádico Option (Some ou None), encapsulando a presença ou ausência de valor com funções .map() e .flat_map().
Requisitos de Execução:
- Implemente a classe abstrata
Option[T]com subclassesSome[T]eNoneVal. - Implemente os métodos monádicos
.map(fn)e.get_or_else(default). - Construa uma consulta aninhada em um repositório imutável de usuários (buscar usuário $\rightarrow$ buscar endereço $\rightarrow$ extrair CEP) de modo 100% seguro sem nenhuma instrução
if obj is not None.
Resultado Esperado
Consulta 1 (Usuário Completo):
-> CEP Encontrado: '01310-100'
Consulta 2 (Usuário sem endereço cadastrado):
-> CEP Padrão Retornado com Segurança: 'CEP_NAO_INFORMADO'
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_03_4_SeuNome.py - Envie na tarefa:
Atividade Cap 03 - Paradigma Funcional e Imutabilidade
🔑 Gabarito de Código & Solução Comentada
# Atividade_03_4_Gabarito.py
from typing import Generic, TypeVar, Callable, Any
T = TypeVar('T')
U = TypeVar('U')
class Option(Generic[T]):
def is_present(self) -> bool: raise NotImplementedError
def map(self, fn: Callable[[T], U]) -> 'Option[U]': raise NotImplementedError
def get_or_else(self, default: T) -> T: raise NotImplementedError
class Some(Option[T]):
def __init__(self, value: T):
self._value = value
def is_present(self) -> bool: return True
def map(self, fn: Callable[[T], U]) -> Option[U]:
res = fn(self._value)
return NoneVal() if res is None else Some(res)
def get_or_else(self, default: T) -> T: return self._value
class NoneVal(Option[Any]):
def is_present(self) -> bool: return False
def map(self, fn: Callable[[Any], Any]) -> Option[Any]: return self
def get_or_else(self, default: Any) -> Any: return default
if __name__ == "__main__":
db_usuarios = {
1: {"nome": "Mariana", "endereco": {"cep": "01310-100"}},
2: {"nome": "Roberto", "endereco": None}
}
def consultar_cep(user_id: int) -> str:
return (Some(db_usuarios.get(user_id))
.map(lambda u: u.get("endereco"))
.map(lambda e: e.get("cep"))
.get_or_else("CEP_NAO_INFORMADO"))
print("Consulta 1 (Usuário Completo):")
print(f"-> CEP Encontrado: '{consultar_cep(1)}'")
print("Consulta 2 (Usuário sem endereço cadastrado):")
print(f"-> CEP Padrão Retornado com Segurança: '{consultar_cep(2)}'")