Como fazer arrays crescentes em Lua na prática
Quando alguém começa a programar em Lua, a primeira coisa que descobre é que as tabelas não são vetores fixos. Elas crescem conforme você insere dados. O nome "lua quarto crescente" aparece bastante nos fóruns porque descreve exatamente esse padrão: criar uma sequência de valores usando operações incrementais como table.insert(), indexação por #t ou até mesmo construtores de tabela com expansão dinâmica. Não existe uma função mágica chamada "quarto crescente". É apenas uma descrição informal do jeito que tabelas indexadas por números se comportam quando você as preenche passo a passo.
Entendendo lua quarto crescente
O conceito é simples. Você declara uma tabela vazia e vai adicionando elementos ao longo da execução do seu código. O índice numérico é implicitamente gerado pelo interpretador. A forma mais básica que eu vejo todo mundo usar:
t = {}\nt[#t + 1] = valor\n Essa abordagem funciona. O operador # retorna o tamanho da parte sequencial da tabela. Você soma 1 e atribui. Simples assim. O problema é que ela tem armadilhas que ninguém te avisa na documentação oficial.
Se você deletar um elemento do meio, o tamanho não volta ao que era. A parte sequencial da tabela fica com buracos. E #t pode retornar um valor menor que o esperado ou, em alguns casos extremos, nem ser determinístico se a tabela tiver partes não sequenciais misturadas.
O problema que eu encontrei na prática
Estava construindo um sistema de log incremental que acumulava mensagens em uma tabela, processava em lotes e depois limpava. Usava #t para saber onde inserir o próximo registro. Num certo momento, comecei a deletar entradas individuais pelo índice antes de processar o lote. O tamanho passado a funcionar de envio ficava cada vez menor do que o esperado. Mensagens sumiam. Não por erro de lógica de negócio, mas porque #t estava retornando índice errado. A solução foi abandonar o operador # para controle de inserção e passar a usar uma variável separada que controla o próximo índice. Assim:
👉 Clique no botão abaixo para saber mais sobre o assunto!
t = {}\nn = 0\nn = n + 1\nt[n] = valor\n Assim nunca depende do # para saber onde colocar o próximo elemento. O valor de n é a única fonte de verdade. Quando preciso limpar, posso fazer for i = 1, n do t[i] = nil end; n = 0. Funciona de forma previsível.
Outra forma mais segura de construir
Existe também o table.insert(t, valor), que é equivalente a t[#t + 1] = valor, mas com uma pequena vantagem: ele funciona corretamente mesmo se houver gaps não inteiros na tabela. Ainda assim, sofre do mesmo problema de # quando você remove elementos arbitrariamente. Para situações onde você quer crescer uma tabela de forma previsível sem depender de contagem automática, uma opção mais robusta é usar uma lista ligada customizada. Cria-se uma tabela com campos next e value. Isso elimina qualquer ambiguidade de tamanho. Mas é overkill para a maioria dos casos.
Pitfalls avançados que iniciantes ignoram
O operador # em tabelas com holes (buracos) não é especificado pela linguagem para retornar qualquer valor em particular desde Lua 5.3. Isso significa que seu código pode passar no teste local e falhar em outra versão do interpretador. Se você confia no # como fonte de índice real, está pedindo para ter problemas futuros. Outro ponto: tabelas grandes crescem exponencialmente de capacidade interna. Isso é bom para performance, mas ruim para memória. Se você está acumulando milhares de itens e depois descarta metade, a tabela continua alocada com espaço para o dobro. Um table.create com contagem estimada pode ajudar se você souber a escala antecipadamente.
Quando lua quarto crescente não funciona bem
Se o padrão exige remoções frequentes no início ou no meio, esse approach vira um pesadelo de complexidade O(n) em cada operação. Nesse cenário, prefira uma fila circular, um deque customizado, ou simplesmente uma tabela associativa com chaves explicitamente gerenciadas. Tabelas crescentes não foram feitas para operações de remoção arbitrária com frequência. Também não adianta usar esse padrão para estruturar dados com ordenação semântica. Se a ordem importa de verdade, use table.sort() após inserir, mas saiba que isso é O(n log n) e pode ser caro em tabelas grandes. Às vezes vale mais a pena manter a tabela sempre ordenada durante a inserção usando busca binária.
Resumo rápido do que funciona
Use contagem manual com variável separada quando precisar inserir e remover dinamicamente. Confie em #t apenas para tabelas estritamente append-only. E evite misturar partes sequenciais com partes associativas na mesma tabela se quiser comportamento previsível do operador tamanho.