2007-04-06

Alterando o comportamento de strings em Lua on-the-fly

Lua Um recurso legal de Lua é permitir a alteração do comportamento de strings «on-the-fly».

Por exemplo, strings em Lua não possuem métodos para «capitalizar» e «normalizar», mas podemos criar.

Por exemplo:

> var = "la batalema pitonisto"
> print(var:normalize())
stdin:1: attempt to call method 'normalize' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: ?


Pois é… não existe. Mas podemos criar!

Nem precisa reiniciar o interpetador. Vamos continuar daí e criar nosso normalize():
> function string:normalize()
>> local t = {}
>> local naocapitular = { "da", "das", "de", "do", "dos", "e" }
>> self:gsub("(%S+)", function (e)
>> e = e:lower()
>> local alterar, lig = true
>> for _, lig in ipairs(naocapitular) do
>> if e == lig then alterar = false end
>> end
>> if alterar then
>> e = e:sub(1, 1):upper() .. e:sub(2)
>> end
>> table.insert(t, e)
>> end)
>> return table.concat(t, " ")
>> end


Vamos analisar…

A tabela naocapitular contém uma lista dos elementos que não queremos capitular. Se quiser, você pode acrescentar outros termos de ligação, como "el", "von", "van", "and", "du", etc..

A chamada do método gsub() de self seleciona cada palavra ("(%S+)") e executa a função passada como segundo parâmetro para cada ocorrência.

A função transforma todas letras de cada palavra para minúsculas (e:lower()), depois corre um for para verificar se a palavra atual devem ou não ser capituladas.

Se a palavra deve ser capitulada, transforma a primeira letra em maiúscula (e:sub(1, 1):upper()), mantendo as demais (e:sub(2)).

Finalmente a palavra é inserida na tabela temporária.

Já no final de normalize(), o return retorna uma string, concatenando os elementos de t usando um espaço (" ") como separador.

Bem, feito isso, é só pegar o mesmo comando anterior:
> print(var:normalize())
La Batalema Pitonisto


Olha só! A string que já existia passou a ter o método normalize()!

Isso aconteceu devido à forma como Lua trata os objetos: metatabelas!

Toda string possui como metatabela uma tabela cuja chave __index aponta para o módulo string:
> print(getmetatable("").__index == string)
true


Então quando a função normalize() foi acrescentada ao módulo string, esta passou a ser método de todas as strings, mesmo as já criadas!

Com isso em mãos, as possibilidades são enormes. =)

[]'s

PS: Veja este artigo também no Kodumaro.