org - ¿Cómo iterar personajes individuales en la secuencia de Lua?
lua roblox (5)
Tengo una cadena en Lua y quiero iterar caracteres individuales en ella. Pero ningún código que he probado funciona y el manual oficial solo muestra cómo encontrar y reemplazar las subcadenas :(
str = "abcd"
for char in str do -- error
print( char )
end
for i = 1, str:len() do
print( str[ i ] ) -- nil
end
Dependiendo de la tarea en cuestión, podría ser más fácil usar string.byte
. También es la forma más rápida, ya que evita la creación de una nueva subcadena que resulta bastante costosa en Lua gracias a la creación de hash de cada nueva cadena y comprobar si ya se conoce. Puede precalcular el código de símbolos que busca con el mismo string.byte
para mantener la legibilidad y la portabilidad.
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
En lua 5.1, puede repetir los caracteres de una cadena de esta forma.
El ciclo básico sería:
for i = 1, #str do local c = str:sub(i,i) -- do something with c end
Pero puede ser más eficiente usar un patrón con string.gmatch()
para obtener un iterador sobre los caracteres:
for c in str:gmatch"." do -- do something with c end
O incluso para usar string.gsub()
para llamar a una función para cada char:
str:gsub(".", function(c) -- do something with c end)
En todo lo anterior, he aprovechado el hecho de que el módulo de string
se establece como un metatabla para todos los valores de cadena, por lo que sus funciones se pueden llamar como miembros usando la notación :
También utilicé el (nuevo en 5.1, IIRC) #
para obtener la longitud de la cuerda.
La mejor respuesta para su aplicación depende de muchos factores, y los puntos de referencia son su amigo si el rendimiento va a importar.
Es posible que desee evaluar por qué necesita iterar sobre los caracteres, y observar uno de los módulos de expresiones regulares que se han vinculado a Lua, o un enfoque de enfoque moderno en el módulo lpeg de Roberto que implementa Parsing Expression Grammers para Lua.
Si está usando Lua 5, intente:
for i = 1, string.len(str) do
print( string.sub(str, i, i) )
end
Todas las personas sugieren un método menos óptimo
Será lo mejor:
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
Ya hay muchos enfoques buenos en las respuestas proporcionadas ( here , here y here ). Si lo que buscas es velocidad, definitivamente deberías considerar hacer el trabajo a través de la API C de Lua, que es mucho más rápido que el código Lua sin procesar. Cuando se trabaja con trozos precargados (por ejemplo , función de carga ), la diferencia no es tan grande, pero aún es considerable.
En cuanto a las soluciones Lua puras , permítanme compartir este pequeño punto de referencia, lo he hecho. Cubre todas las respuestas proporcionadas hasta la fecha y agrega algunas optimizaciones. Aún así, lo básico a considerar es:
¿Cuántas veces necesitará iterar sobre los caracteres en una cadena?
- Si la respuesta es "una vez", entonces debe buscar la primera parte de la marca ("velocidad bruta").
- De lo contrario, la segunda parte proporcionará una estimación más precisa, ya que analiza la cadena en la tabla, que es mucho más rápida de iterar. También debería considerar escribir una función simple para esto, como sugirió @Jarriz.
Aquí está el 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))
Ejemplo de salida (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:
En mi caso, string.byte
y string.sub
fueron los más rápidos en términos de velocidad bruta. Al usar la tabla de caché y reutilizarla 10 veces por ciclo, la versión de string.byte
fue más rápida incluso al convertir los códigos de caracteres en caracteres (lo que no siempre es necesario y depende del uso).
Como probablemente haya notado, hice algunas suposiciones basadas en mis puntos de referencia anteriores y las apliqué al código:
- Las funciones de la biblioteca siempre deben estar localizadas si se utilizan dentro de los bucles, porque es mucho más rápido.
- Insertar nuevo elemento en la tabla lua es mucho más rápido usando
tbl[idx] = value
quetable.insert(tbl, value)
. - Alinear a través de la tabla usando
for i = 1, #tbl
es un poco más rápido quefor k, v in pairs(tbl)
. - Siempre prefiera la versión con menos llamadas a función, porque la llamada en sí misma agrega un poco al tiempo de ejecución.
Espero eso ayude.