Como iterar caracteres individuais em seqüência Lua?
-
06-07-2019 - |
Pergunta
Eu tenho uma corda em Lua e quero fazer uma iteração caracteres individuais nele. Mas nenhum código eu tentei obras e os únicos shows oficiais manuais como localizar e substituir textos: (
str = "abcd"
for char in str do -- error
print( char )
end
for i = 1, str:len() do
print( str[ i ] ) -- nil
end
Solução
Em Lua 5.1, você pode iterar dos caracteres de uma string isso em um par de formas.
O ciclo básico seria:
for i = 1, #str do local c = str:sub(i,i) -- do something with c end
Mas pode ser mais eficiente usar um padrão com string.gmatch()
para obter um iterador sobre os personagens:
for c in str:gmatch"." do -- do something with c end
Ou até mesmo para uso string.gsub()
para chamar uma função para cada caractere:
str:gsub(".", function(c) -- do something with c end)
Em todas as opções acima, eu tenho aproveitado o fato de que o módulo string
é definido como uma metatabela para todos os valores de cadeia, por isso, suas funções pode ser chamado como membros usando a notação :
. Eu também usou o (novo para 5.1, IIRC) #
para obter o comprimento string.
A melhor resposta para a sua aplicação depende de uma série de fatores, e benchmarks são seu amigo se o desempenho vai importar.
Você pode querer avaliar por você precisa iterar sobre os personagens, e olhar para um dos módulos de expressão regular que foram obrigados a Lua, ou para um olhar abordagem moderna para Roberto de < a href = "http://www.inf.puc-rio.br/~roberto/lpeg.html" rel = "noreferrer"> LPEG módulo que implementa Parsing Grammers expressão para Lua.
Outras dicas
Se você estiver usando Lua 5, tente:
for i = 1, string.len(str) do
print( string.sub(str, i, i) )
end
Dependendo da tarefa em mãos pode ser mais fácil de usar string.byte
. É também das maneiras mais rápidas porque evita a criação de nova substring que happends a ser bastante caro na Lua graças a hash de cada nova sequência e verificar se ele já é conhecido. Você pode pré-calcular código de símbolos que você procura com o mesmo string.byte
para manter a legibilidade e portabilidade.
local str = "ab/cd/ef"
local target = string.byte("/")
for idx = 1, #str do
if str:byte(idx) == target then
print("Target found at:", idx)
end
end
Já há um monte de boas abordagens nas respostas fornecidas ( aqui , here e aqui ). Se a velocidade é o que você principalmente procurando, você deve definitivamente considerar fazer o trabalho por meio da API C da Lua, que é muitas vezes mais rápido do que o código Lua cru. Quando se trabalha com os pedaços pré-carregadas (por ex. carga função ), a diferença não é tão grande, mas ainda considerável.
Quanto aos pura soluções Lua, deixe-me compartilhar esta pequena referência, eu fiz. Abrange todos os resposta dada a esta data e adiciona algumas otimizações. Ainda assim, a primeira coisa a considerar é:
Quantas vezes você vai precisar iterar sobre personagens de corda?
- Se a resposta for "uma vez", que você deve olhar para cima primeira parte do banchmark ( "velocidade crua").
- Caso contrário, a segunda parte irá fornecer estimativa mais precisa, porque analisa a cadeia na tabela, o que é muito mais rápido para iterar. Você também deve considerar a escrever uma função simples para isso, como @Jarriz sugeriu.
Aqui está o código completo:
-- Setup locals
local str = "Hello World!"
local attempts = 5000000
local reuses = 10 -- For the second part of benchmark: Table values are reused 10 times. Change this according to your needs.
local x, c, elapsed, tbl
-- "Localize" funcs to minimize lookup overhead
local stringbyte, stringchar, stringsub, stringgsub, stringgmatch = string.byte, string.char, string.sub, string.gsub, string.gmatch
print("-----------------------")
print("Raw speed:")
print("-----------------------")
-- Version 1 - string.sub in loop
x = os.clock()
for j = 1, attempts do
for i = 1, #str do
c = stringsub(str, i)
end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))
-- Version 2 - string.gmatch loop
x = os.clock()
for j = 1, attempts do
for c in stringgmatch(str, ".") do end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))
-- Version 3 - string.gsub callback
x = os.clock()
for j = 1, attempts do
stringgsub(str, ".", function(c) end)
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))
-- For version 4
local str2table = function(str)
local ret = {}
for i = 1, #str do
ret[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert
end
return ret
end
-- Version 4 - function str2table
x = os.clock()
for j = 1, attempts do
tbl = str2table(str)
for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop.
c = tbl[i]
end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))
-- Version 5 - string.byte
x = os.clock()
for j = 1, attempts do
tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character.
for i = 1, #tbl do
c = tbl[i] -- Note: produces char codes instead of chars.
end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))
-- Version 5b - string.byte + conversion back to chars
x = os.clock()
for j = 1, attempts do
tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character.
for i = 1, #tbl do
c = stringchar(tbl[i])
end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))
print("-----------------------")
print("Creating cache table ("..reuses.." reuses):")
print("-----------------------")
-- Version 1 - string.sub in loop
x = os.clock()
for k = 1, attempts do
tbl = {}
for i = 1, #str do
tbl[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))
-- Version 2 - string.gmatch loop
x = os.clock()
for k = 1, attempts do
tbl = {}
local tblc = 1 -- Note: This is faster than table.insert
for c in stringgmatch(str, ".") do
tbl[tblc] = c
tblc = tblc + 1
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))
-- Version 3 - string.gsub callback
x = os.clock()
for k = 1, attempts do
tbl = {}
local tblc = 1 -- Note: This is faster than table.insert
stringgsub(str, ".", function(c)
tbl[tblc] = c
tblc = tblc + 1
end)
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))
-- Version 4 - str2table func before loop
x = os.clock()
for k = 1, attempts do
tbl = str2table(str)
for j = 1, reuses do
for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop.
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))
-- Version 5 - string.byte to create table
x = os.clock()
for k = 1, attempts do
tbl = {stringbyte(str,1,#str)}
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))
-- Version 5b - string.byte to create table + string.char loop to convert bytes to chars
x = os.clock()
for k = 1, attempts do
tbl = {stringbyte(str, 1, #str)}
for i = 1, #tbl do
tbl[i] = stringchar(tbl[i])
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))
Exemplo de saída (Lua 5.3.4, Windows) :
-----------------------
Raw speed:
-----------------------
V1: elapsed time: 3.713
V2: elapsed time: 5.089
V3: elapsed time: 5.222
V4: elapsed time: 4.066
V5: elapsed time: 2.627
V5b: elapsed time: 3.627
-----------------------
Creating cache table (10 reuses):
-----------------------
V1: elapsed time: 20.381
V2: elapsed time: 23.913
V3: elapsed time: 25.221
V4: elapsed time: 20.551
V5: elapsed time: 13.473
V5b: elapsed time: 18.046
Resultado:
No meu caso, o string.byte
e string.sub
foram os mais rápidos em termos de velocidade crua. Ao usar tabela de cache e reutilizá-lo 10 vezes por ciclo, a versão string.byte
foi o mais rápido, mesmo quando charCodes conversão de volta para caracteres (o que nem sempre é necessário e depende do uso).
Como você já deve ter notado, eu fiz algumas suposições baseadas em minhas referências anteriores e aplicou-as ao código:
- funções de biblioteca deve sempre ser localizada se os laços dentro usados, porque é muito mais rápido.
- Inserir novo elemento na tabela Lua é muito mais rápido usando
tbl[idx] = value
quetable.insert(tbl, value)
. - looping através da tabela usando
for i = 1, #tbl
é um pouco mais rápido do quefor k, v in pairs(tbl)
. - Sempre prefiro a versão com chamadas de função menos, porque a própria chamada acrescenta um pouco para o tempo de execução.
Hope isso ajuda.
Todas as pessoas sugerem um método menos ideal
Será melhor:
function chars(str)
strc = {}
for i = 1, #str do
table.insert(strc, string.sub(str, i, i))
end
return strc
end
str = "Hello world!"
char = chars(str)
print("Char 2: "..char[2]) -- prints the char 'e'
print("-------------------\n")
for i = 1, #str do -- testing printing all the chars
if (char[i] == " ") then
print("Char "..i..": [[space]]")
else
print("Char "..i..": "..char[i])
end
end