Função: o que é, tipos de funções e gráficos - Toda Matéria
O básico que ninguém explica direito
Funções existem pra evitar que você repita o mesmo código três vezes no mesmo arquivo. Isso é óbvio, todo mundo fala isso. O que as pessoas não explicam é que a maioria dos iniciantes escreve funções que fazem tudo e nada ao mesmo tempo, porque acham que função precisa ser curta. Não precisa.
Eu já vi função de 80 linhas que deveria ser três funções de 25 linhas cada. O problema é que quem escreve acha que separar em várias funções diminui a performance. Não diminui. O Python, o JavaScript, o Go — qualquer linguagem razoável otimiza isso no runtime. A única coisa que realmente importa é legibilidade e manutenção.
Pra que serve uma função na prática
Quando você tem um bloco de código que vai aparecer mais de uma vez no seu programa, você transforma ele em função. Ponto. Não tem mistério. Função recebe dados, processa, devolve resultado. Pode não devolver nada também, aí é procedure, mas a maioria das linguagens modernas não faz essa distinção.
A sintaxe varia. Em Python você usa `def`, em JavaScript tem as arrow functions, em Rust tem os closures. O conceito é o mesmo. Você escreve uma vez, chama várias vezes com parâmetros diferentes.
O erro mais comum que eu vejo é gente criar função sem parâmetro porque esqueceu que função precisa receber dados pra fazer algo útil. Função que não recebe nada e não retorna nada basicamente é um script executado em cima do nada. Só funciona se o efeito colateral for o objetivo principal.
O que são funções e por que elas complicam sua vida no início
Eu lembro quando comecei a programar e achava que função era mágica. Eu criava uma função pra calcular média, passava dois números, o resultado aparecia. Parece inocente, mas tem um detalhe que pega todo mundo: escopo.
Variável criada dentro da função não existe fora dela. Isso é intenção do design da linguagem, não bug. Mas no começo você escreve `resultado = calcular_algo()`, a função define `resultado` internamente, e quando você tenta imprimir lá fora, dá erro. Não porque a função falhou, mas porque `resultado` é diferente dentro e fora do escopo.
Eu levei três meses pra parar de confundir isso. A solução prática é nomear variáveis de forma explícita. Em vez de `x`, use `valor_calculado`. Quando você ler o código seis meses depois, vai agradecer.
Outro problema real que eu enfrentei foi recursão sem condição de parada. Eu escrevi uma função que chamava a si mesma pra percorrer uma estrutura de dados aninhada. Achei elegante. O Python tem limite de recursão de 1000 chamadas. Quando minha estrutura tinha mais de 1000 níveis, o programa travava com `RecursionError`. A solução foi transformar em iteração. Funções recursivas são bonitas, mas limitadas por recursos da linguagem.
Edge cases que ninguém conta
Funções com mutabilidade são uma dor de cabeça que aparece quando você menos espera. Eu passei duas semanas debugging um problema onde uma função modificava uma lista que eu havia passado como argumento. A lista original mudava, e eu não entendia por quê.
Em Python, listas são passadas por referência. Se a função faz `lista.append()`, a lista original muda. A solução foi passar uma cópia: `lista.copy()` ou usar slicing `lista[:]`. Em JavaScript o problema é similar com objetos e arrays. A solução geral é não mutar argumentos, retornar novos valores.
Parâmetros com valor padrão também causam confusão. Em Python, se você faz `def funcao(lista=[])`, a lista é criada uma vez e reutilizada em todas as chamadas. Isso significa que valores persistem entre chamadas de função. O workaround é usar `None` e criar dentro da função: `if lista is None: lista = []`.
Tipagem dinâmica esconde bugs até você testar. Eu tinha uma função que esperava número, recebia string, e só dava erro em runtime quando o código chegava numa operação matemática. Solutions like type hints no Python ou interfaces em TypeScript ajudam, mas não resolvem completamente. Testes unitários são a única defesa real.
Quando função não é a resposta
Funções não são sempre a solução certa. Se você tem um bloco de código que usa apenas uma vez, transformar em função aumenta complexidade sem benefício. O overhead de chamar função existe, mesmo que pequeno. Em laços apertados que rodam milhões de vezes, o custo de chamada pode ser relevante.
Em Python, cada chamada de função custa cerca de 100-200 nanosegundos. Em scripts pequenos não importa. Em processamento de imagem que itera sobre pixels, pode somar segundos a mais no total. Aí às vezes inline o código ou usa otimizações específicas.
Funções muito grandes são sinal de design ruim. Se sua função passa de 50 linhas, provavelmente está fazendo muitas coisas. Separe em funções menores. Cada função deve ter uma responsabilidade clara. Nomeie ela de forma que descreva o que faz, não como faz.
Eu já revi código de equipe onde função chamava função chamava função, e ninguém sabia o que o código fazia no total. A solução foi mapear o fluxo, identificar responsabilidades, e redistribuir em funções menores. O resultado foi código que eu consegui entender depois de dois meses sem estar nele.
Dica prática que economiza horas
Documente suas funções desde o início. Docstring em Python, JSDoc em JavaScript. Três linhas que descrevem parâmetros, retorno e efeito colateral. Isso parece burocracia, mas quando você volta no código seis meses depois, agradece.
Teste suas funções isoladamente. Função pura, sem efeitos colaterais, é fácil de testar. Função com side effects exige setup complexo. Separe lógica de cálculo de lógica de I/O. Chame a função de cálculo com dados mockados. Chame a função de I/O separadamente.
Performance tuning em funções vem depois de medição. Não otimize antes de saber onde está o gargalo. Use profiling. Em Python, `cProfile` ou `timeit`. Em JavaScript, `console.time`. Medir antes de otimizar evita perder tempo em otimizações inúteis.
O mercado tem frameworks que geram funções automaticamente. ORM, generators, scaffolding. Eles ajudam, mas não substituem entendimento do básico. Quando o código gerado falha, você precisa saber consertar. Funções são bloco de construção fundamental. Dominar elas é requisito, não diferencial.