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:

  1. Receba uma tupla imutável de pontuações de alunos: (4.5, 8.0, 6.5, 9.2, 3.0, 7.5).
  2. Escreva uma função pura bonificar_aprovados_puro(notas: tuple, nota_corte: float, bonus: float) -> tuple.
  3. 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)

  1. Salve o arquivo como: Atividade_03_1_SeuNome.py
  2. 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:

  1. Crie uma função curried para cálculo de juros compostos: calcular_montante(taxa)(periodos)(capital).
  2. 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)

  1. Salve o arquivo como: Atividade_03_2_SeuNome.py
  2. 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:

  1. Crie a função de alta ordem pipe(*funcoes) que combine transformações da esquerda para a direita.
  2. 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)

  1. Salve o arquivo como: Atividade_03_3_SeuNome.py
  2. 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:

  1. Implemente a classe abstrata Option[T] com subclasses Some[T] e NoneVal.
  2. Implemente os métodos monádicos .map(fn) e .get_or_else(default).
  3. 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)

  1. Salve o arquivo como: Atividade_03_4_SeuNome.py
  2. 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)}'")