W lua 5.1 możesz iterować znaki łańcucha na kilka sposobów.
Podstawowa pętla wyglądałaby tak:
dla i = 1, #str do
lokalny c = str: sub (i, i)
- zrób coś z c
koniec
Ale wydajniejsze może być użycie wzorca z, string.gmatch()
aby uzyskać iterator po znakach:
dla c in str: gmatch "." zrobić
- zrób coś z c
koniec
Lub nawet użyć string.gsub()
do wywołania funkcji dla każdego znaku:
str: gsub (".", funkcja (c)
- zrób coś z c
koniec)
We wszystkich powyższych przypadkach wykorzystałem fakt, że string
moduł jest ustawiony jako metatable dla wszystkich wartości łańcuchowych, więc jego funkcje można wywoływać jako elementy :
składowe przy użyciu notacji. Użyłem również (nowość w 5.1, IIRC), #
aby uzyskać długość struny.
Najlepsza odpowiedź dla Twojej aplikacji zależy od wielu czynników, a testy porównawcze są Twoim przyjacielem, jeśli wydajność ma mieć znaczenie.
Możesz chcieć ocenić, dlaczego musisz iterować po znakach i przyjrzeć się jednemu z modułów wyrażeń regularnych, które zostały powiązane z Lua, lub przyjrzeć się nowemu podejściu do modułu lpeg Roberta , który implementuje Parsing Expression Grammers for Lua.
Jeśli używasz Lua 5, spróbuj:
for i = 1, string.len(str) do print( string.sub(str, i, i) ) end
źródło
W zależności od wykonywanego zadania może być łatwiejszy w użyciu
string.byte
. Jest to również najszybszy sposób, ponieważ pozwala uniknąć tworzenia nowego podciągu, który zdarza się być dość drogi w Lua, dzięki haszowaniu każdego nowego ciągu i sprawdzaniu, czy jest już znany. Możesz wstępnie obliczyć kod symboli, których szukasz,string.byte
aby zachować czytelność i przenośność.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
źródło
W udzielonych odpowiedziach ( tutaj , tutaj i tutaj ) jest już wiele dobrych podejść . Jeśli przede wszystkim szukasz szybkości , zdecydowanie powinieneś rozważyć wykonanie tej pracy za pośrednictwem interfejsu API Lua C, który jest wielokrotnie szybszy niż surowy kod Lua. Podczas pracy z wstępnie załadowanymi fragmentami (np. Funkcja ładowania ) różnica nie jest tak duża, ale nadal znaczna.
Jeśli chodzi o czyste rozwiązania Lua, podzielę się tym małym testem, który stworzyłem. Obejmuje każdą udzieloną odpowiedź do tej daty i dodaje kilka optymalizacji. Jednak podstawową rzeczą do rozważenia jest:
Ile razy będziesz potrzebować iterować po znakach w ciągu?
Oto pełny kod:
-- 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))
Przykładowe dane wyjściowe (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
Wynik:
W moim przypadku
string.byte
istring.sub
były najszybsze pod względem szybkości surowej. Podczas korzystania z tabeli pamięci podręcznej i ponownego jej używania 10 razy na pętlę,string.byte
wersja była najszybsza nawet przy konwersji kodów znaków z powrotem na znaki (co nie zawsze jest konieczne i zależy od użycia).Jak zapewne zauważyłeś, poczyniłem pewne założenia w oparciu o moje poprzednie testy porównawcze i zastosowałem je do kodu:
tbl[idx] = value
niżtable.insert(tbl, value)
.for i = 1, #tbl
jest nieco szybsze niżfor k, v in pairs(tbl)
.Mam nadzieję, że to pomoże.
źródło
Wszyscy sugerują mniej optymalną metodę
Najlepiej będzie:
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
źródło
Iterowanie w celu skonstruowania łańcucha i zwrócenie tego ciągu jako tabeli za pomocą funkcji load () ...
itab=function(char) local result for i=1,#char do if i==1 then result=string.format('%s','{') end result=result..string.format('\'%s\'',char:sub(i,i)) if i~=#char then result=result..string.format('%s',',') end if i==#char then result=result..string.format('%s','}') end end return load('return '..result)() end dump=function(dump) for key,value in pairs(dump) do io.write(string.format("%s=%s=%s\n",key,type(value),value)) end end res=itab('KOYAANISQATSI') dump(res)
Wystawia ...
1=string=K 2=string=O 3=string=Y 4=string=A 5=string=A 6=string=N 7=string=I 8=string=S 9=string=Q 10=string=A 11=string=T 12=string=S 13=string=I
źródło