Wie iteriere ich einzelne Zeichen in der Lua-Zeichenfolge?
87
Ich habe eine Zeichenfolge in Lua und möchte einzelne Zeichen darin wiederholen. Aber kein Code, den ich ausprobiert habe, funktioniert und das offizielle Handbuch zeigt nur, wie Teilzeichenfolgen gefunden und ersetzt werden :(
str ="abcd"for char in str do-- error
print( char )endfor i =1, str:len()do
print( str[ i ])-- nilend
In Lua 5.1 können Sie die Zeichen einer Zeichenfolge auf verschiedene Arten durchlaufen.
Die Grundschleife wäre:
für i = 1 ist #str do
lokal c = str: sub (i, i)
- etwas mit c machen
Ende
Es kann jedoch effizienter sein, ein Muster zu verwenden string.gmatch(), um einen Iterator über die Zeichen zu erhalten:
für c in str: gmatch "." machen
- etwas mit c machen
Ende
Oder sogar, um string.gsub()eine Funktion für jedes Zeichen aufzurufen:
str: gsub (".", Funktion (c)
- etwas mit c machen
Ende)
In all dem habe ich die Tatsache ausgenutzt, dass das stringModul als Metatable für alle Zeichenfolgenwerte festgelegt ist, sodass seine Funktionen unter Verwendung der :Notation als Mitglieder aufgerufen werden können . Ich habe auch die (neu in 5.1, IIRC) verwendet #, um die Stringlänge zu erhalten.
Die beste Antwort für Ihre Anwendung hängt von vielen Faktoren ab, und Benchmarks sind Ihr Freund, wenn es auf die Leistung ankommt.
Vielleicht möchten Sie bewerten, warum Sie die Zeichen durchlaufen müssen, und sich eines der an Lua gebundenen Module für reguläre Ausdrücke ansehen oder einen modernen Ansatz für das LPEG- Modul von Roberto ansehen, das Parsing Expression Grammers für Lua implementiert.
Vielen Dank. Über das von Ihnen erwähnte LPEG-Modul: Speichert es nach der Tokenisierung die Token-Positionen im Originaltext? Die Aufgabe, die ich ausführen muss, besteht darin, eine bestimmte einfache Sprache in scite via lua (ohne kompilierten c ++ - Parser) in der Syntax hervorzuheben. Wie installiere ich lpeg? Scheint, als hätte es eine .c-Quelle in der Distribution - muss es zusammen mit lua kompiliert werden?
Grigoryvp
Wenn Sie lpeg erstellen, wird eine DLL (oder .so) erstellt, die dort gespeichert werden soll, wo sie benötigt wird. (dh irgendwo, der durch den Inhalt des globalen package.cpath in Ihrer lua-Installation identifiziert wird.) Sie müssen auch das Begleitmodul re.lua installieren, wenn Sie die vereinfachte Syntax verwenden möchten. Mit einer LPEG-Grammatik können Sie Rückrufe abrufen und Text auf verschiedene Arten erfassen, und es ist sicherlich möglich, Captures zu verwenden, um einfach den Ort der Übereinstimmung für die spätere Verwendung zu speichern. Wenn Syntax-Highlight das Ziel ist, ist ein PEG keine schlechte Wahl.
RBerteig
3
Ganz zu schweigen von den neuesten Versionen von SciTE (seit 2.22), einschließlich Scintillua, einem LPEG-basierten Lexer, was bedeutet, dass es sofort funktioniert, ohne dass eine Neukompilierung erforderlich ist.
Stuart P. Bentley
11
Wenn Sie Lua 5 verwenden, versuchen Sie:
for i =1, string.len(str)do
print( string.sub(str, i, i))end
Abhängig von der jeweiligen Aufgabe ist die Verwendung möglicherweise einfacher string.byte. Dies ist auch der schnellste Weg, da dadurch vermieden wird, dass neue Teilzeichenfolgen erstellt werden, die in Lua sehr teuer werden, da jede neue Zeichenfolge gehasht und überprüft wird, ob sie bereits bekannt ist. Sie können den Code der gesuchten Symbole vorab berechnen string.byte, um die Lesbarkeit und Portabilität zu gewährleisten.
local str ="ab/cd/ef"local target = string.byte("/")for idx =1,#str doif str:byte(idx)== target then
print("Target found at:", idx)endend
In den bereitgestellten Antworten gibt es bereits viele gute Ansätze ( hier , hier und hier ). Wenn Sie in erster Linie nach Geschwindigkeit suchen, sollten Sie auf jeden Fall in Betracht ziehen, die Arbeit über die C-API von Lua zu erledigen, die um ein Vielfaches schneller ist als roher Lua-Code. Bei der Arbeit mit vorinstallierten Chunks (z. B. Ladefunktion ) ist der Unterschied nicht so groß, aber dennoch beträchtlich.
Lassen Sie mich bei den reinen Lua-Lösungen diesen kleinen Benchmark teilen, den ich erstellt habe. Es deckt jede Antwort auf dieses Datum ab und fügt einige Optimierungen hinzu. Das Grundlegende ist jedoch:
Wie oft müssen Sie Zeichen in Zeichenfolgen durchlaufen?
Wenn die Antwort "einmal" lautet, sollten Sie den ersten Teil des Banchmarks nachschlagen ("rohe Geschwindigkeit").
Andernfalls liefert der zweite Teil eine genauere Schätzung, da die Zeichenfolge in die Tabelle analysiert wird, was viel schneller zu durchlaufen ist. Sie sollten auch in Betracht ziehen, eine einfache Funktion dafür zu schreiben, wie von @Jarriz vorgeschlagen.
Hier ist der vollständige Code:
-- Setup localslocal str ="Hello World!"local attempts =5000000local 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 overheadlocal 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 dofor i =1,#str do
c = stringsub(str, i)endend
elapsed = os.clock()- x
print(string.format("V1: elapsed time: %.3f", elapsed))-- Version 2 - string.gmatch loop
x = os.clock()for j =1, attempts dofor c in stringgmatch(str,".")doendend
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 4local str2table =function(str)local ret ={}for i =1,#str do
ret[i]= stringsub(str, i)-- Note: This is a lot faster than using table.insertendreturn 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]endend
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.endend
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])endend
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.insertendfor j =1, reuses dofor i =1,#tbl do
c = tbl[i]endendend
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.insertfor c in stringgmatch(str,".")do
tbl[tblc]= c
tblc = tblc +1endfor j =1, reuses dofor i =1,#tbl do
c = tbl[i]endendend
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 +1end)for j =1, reuses dofor i =1,#tbl do
c = tbl[i]endendend
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 dofor i =1,#tbl do-- Note: This type of loop is a lot faster than "pairs" loop.
c = tbl[i]endendend
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 dofor i =1,#tbl do
c = tbl[i]endendend
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])endfor j =1, reuses dofor i =1,#tbl do
c = tbl[i]endendend
elapsed = os.clock()- x
print(string.format("V5b: elapsed time: %.3f", elapsed))
In meinem Fall waren die string.byteund string.subin Bezug auf die Rohgeschwindigkeit am schnellsten. Bei Verwendung der Cache-Tabelle und deren string.bytezehnmaliger Wiederverwendung pro Schleife war die Version selbst beim Konvertieren von Zeichencodes zurück in Zeichen am schnellsten (was nicht immer erforderlich ist und von der Verwendung abhängt).
Wie Sie wahrscheinlich bemerkt haben, habe ich einige Annahmen getroffen, die auf meinen vorherigen Benchmarks basieren, und sie auf den Code angewendet:
Bibliotheksfunktionen sollten immer lokalisiert werden, wenn sie in Schleifen verwendet werden, da sie viel schneller sind.
Das Einfügen eines neuen Elements in die Lua-Tabelle ist mit viel schneller tbl[idx] = valueals table.insert(tbl, value).
Das Durchlaufen der Tabelle mit for i = 1, #tblist etwas schneller als for k, v in pairs(tbl).
Bevorzugen Sie immer die Version mit weniger Funktionsaufrufen, da der Aufruf selbst die Ausführungszeit etwas verlängert.
Alle Menschen schlagen eine weniger optimale Methode vor
Wird am besten sein:
function chars(str)
strc ={}for i =1,#str do
table.insert(strc, string.sub(str, i, i))endreturn 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 charsif(char[i]==" ")then
print("Char "..i..": [[space]]")else
print("Char "..i..": "..char[i])endend
Wenn Sie Lua 5 verwenden, versuchen Sie:
quelle
Abhängig von der jeweiligen Aufgabe ist die Verwendung möglicherweise einfacher
string.byte
. Dies ist auch der schnellste Weg, da dadurch vermieden wird, dass neue Teilzeichenfolgen erstellt werden, die in Lua sehr teuer werden, da jede neue Zeichenfolge gehasht und überprüft wird, ob sie bereits bekannt ist. Sie können den Code der gesuchten Symbole vorab berechnenstring.byte
, um die Lesbarkeit und Portabilität zu gewährleisten.quelle
In den bereitgestellten Antworten gibt es bereits viele gute Ansätze ( hier , hier und hier ). Wenn Sie in erster Linie nach Geschwindigkeit suchen, sollten Sie auf jeden Fall in Betracht ziehen, die Arbeit über die C-API von Lua zu erledigen, die um ein Vielfaches schneller ist als roher Lua-Code. Bei der Arbeit mit vorinstallierten Chunks (z. B. Ladefunktion ) ist der Unterschied nicht so groß, aber dennoch beträchtlich.
Lassen Sie mich bei den reinen Lua-Lösungen diesen kleinen Benchmark teilen, den ich erstellt habe. Es deckt jede Antwort auf dieses Datum ab und fügt einige Optimierungen hinzu. Das Grundlegende ist jedoch:
Wie oft müssen Sie Zeichen in Zeichenfolgen durchlaufen?
Hier ist der vollständige Code:
Beispielausgabe (Lua 5.3.4, Windows) :
Ergebnis:
In meinem Fall waren die
string.byte
undstring.sub
in Bezug auf die Rohgeschwindigkeit am schnellsten. Bei Verwendung der Cache-Tabelle und derenstring.byte
zehnmaliger Wiederverwendung pro Schleife war die Version selbst beim Konvertieren von Zeichencodes zurück in Zeichen am schnellsten (was nicht immer erforderlich ist und von der Verwendung abhängt).Wie Sie wahrscheinlich bemerkt haben, habe ich einige Annahmen getroffen, die auf meinen vorherigen Benchmarks basieren, und sie auf den Code angewendet:
tbl[idx] = value
alstable.insert(tbl, value)
.for i = 1, #tbl
ist etwas schneller alsfor k, v in pairs(tbl)
.Ich hoffe es hilft.
quelle
Alle Menschen schlagen eine weniger optimale Methode vor
Wird am besten sein:
quelle