Em regex, nem sempre menos é mais

Uso expressões regulares há tantos anos, que hoje para mim é muito natural, diria até automático, ficar compondo uma regex na cabeça, nas mais diversas (e inúteis) situações. Por exemplo, se estou relaxado lendo um artigo e em seu texto aparece alguma repetição, pequena variação ou formato numérico, me pego pensando nos metacaracteres que casariam aquilo.

Tá, é bizarro, eu sei :)

Nos artigos anteriores do blog, falei sobre os equipamentos da Apple e várias vezes precisei citar os três brinquedos pretos: iPod, iPad e iPhone. Ao escrever estes nomes, um alerta apita aqui dentro: Epa! Repetição e variação detectados!

There’s a regex for that :)

Compor uma expressão regular que case os três nomes não é difícil. Uma primeira alternativa, mais simplista, seria usar o metacaractere ou para fazer a lista de palavras permitidas:

iPod|iPad|iPhone

A barra vertical indica que esta expressão casa iPod ou iPad ou iPhone. Simples, não? Fácil de ler e entender. Mas podemos tornar esta expressão menor e mais eficiente.

O segredo para fazer uma boa expressão é saber identificar padrões. Ao olhar com atenção para estas três palavras, percebemos que todas possuem o mesmo prefixo, ou seja, todas iniciam com as letras “iP”:

  • iPod
  • iPad
  • iPhone

Assim, podemos tornar nossa expressão mais eficiente deixando claro que o padrão começa com “iP”, e depois disso, há três alternativas:

iP(od|ad|hone)

Essa nova expressão continua casando as três palavras, mas agora ficou menor e mais rápida. Porém, agora ela ficou mais difícil para ler e entender. Trocamos legibilidade por eficiência.

Aplicando mais uma vez a mesma técnica de identificar padrões e melhorar a expressão, podemos perceber que tanto iPod quanto iPad terminam com a letra “d”. Podemos isolar esta letra, trocando o trecho od|ad por [oa]d:

iP([oa]d|hone)

Continuamos casando as mesmas palavras, porém agora a legibilidade foi pelo ralo. É preciso interpretar a expressão mentalmente para saber quais são as palavras que ela casa. Será que fomos longe demais?

Você sempre deve avaliar se esse tipo de melhoria é mesmo uma vantagem.

  • Era necessário otimizar a expressão?
  • A expressão anterior estava lenta?
  • Todos que darão manutenção nesta expressão saberão entendê-la?
  • O que é mais importante: velocidade ou legibilidade?

Acostume-se a sempre fazer estas perguntas a si mesm[ao] cada vez que for melhorar alguma expressão regular. Você pode se surpreender com as respostas :)

Às vezes, é possível encontrar um meio termo que não prejudica tanto a legibilidade, mas resulta em um ganho na performance.

i(Pod|Pad|Phone)

Nesta expressão, isolei somente a letra “i”, que é um prefixo muito conhecido para os produtos da Apple. Deixei o “P” repetindo em todas as alternativas para não quebrar as palavras. Assim fica clara a separação entre o prefixo “i” e o nome dos produtos: Pod, Pad e Phone.

i(Phone|Pod|Pad)

Como última melhoria, movi o trecho “Phone” para o início das possibilidades, para deixar mais claro que esta é uma lista de palavras com produtos da Apple. As alternativas Pod e Pad são muito parecidas e podem gerar confusão. Deixando o “Phone” no início, ali bem pertinho do “i”, fica mais fácil do leitor enxergar “iPhone” e deduzir o iPod e iPad com mais facilidade.

Agora, comparemos as duas expressões:

iP([oa]d|hone)

i(Phone|Pod|Pad)

A primeira seguiu somente o caminho da eficiência, enquanto a segunda fez um balanço entre eficiência e legibilidade.

Nos meus primeiros anos de expressões regulares, eu faria sempre a primeira, pois é a menor e mais eficiente. Hoje, depois de já ter sofrido muito dando manutenção para minhas próprias expressões enigmáticas do passado, valorizo mais uma expressão legível, melhorando a velocidade somente quando estritamente necessário.

Quem já usa as Funções ZZ há tempos, sabe que seu código era afetado por essa mentalidade do “menor é melhor”. Ainda bem que evoluí :)

Aprenda mais sobre os tópicos deste texto:

— EOF —

Gostou desse texto? Aqui tem mais.