Frage

Ich habe eine Zeichenfolge in Lua und will in ihm einzelne Zeichen iterieren. Aber kein Code Ich habe versucht, Werke und das offizielle Handbuch zeigt nur, wie Substrings zu finden und zu ersetzen: (

str = "abcd"
for char in str do -- error
  print( char )
end

for i = 1, str:len() do
  print( str[ i ] ) -- nil
end
War es hilfreich?

Lösung

In lua 5.1 können Sie die Zeichen eines Strings iterieren dies in ein paar Möglichkeiten.

Der Grund Schleife sei:

for i = 1, #str do
    local c = str:sub(i,i)
    -- do something with c
end

Aber es kann effizienter sein, ein Muster mit string.gmatch() zu verwenden einen Iterator über die Charaktere zu bekommen:

for c in str:gmatch"." do
    -- do something with c
end

oder sogar string.gsub() zu verwenden, um eine Funktion für jedes Zeichen zu nennen:

str:gsub(".", function(c)
    -- do something with c
end)

In allen oben genannten, ich habe sich die Tatsache zunutze gemacht, dass das string Modul als Metatabelle für alle String-Werte festgelegt ist, so kann seine Aufgaben als Mitglieder genannt werden, um die : Notation. Ich habe auch die (neu 5.1, IIRC) # die Stringlänge zu erhalten.

Die beste Antwort für Ihre Anwendung hängt von einer Vielzahl von Faktoren ab, und Benchmarks ist dein Freund, wenn die Leistung an der Materie wird.

Sie können auswerten möchten Warum Sie müssen über die Charaktere zu durchlaufen, und in einem der regulären Ausdruck Module zu suchen, die zu Lua gebunden sind, oder für einen modernen Ansatz Blick in Robertos < a href = "http://www.inf.puc-rio.br/~roberto/lpeg.html" rel = "noreferrer"> lpeg Modul, das Parsing Expression Grammers für Lua implementiert.

Andere Tipps

Wenn Sie mit Lua 5, versuchen Sie:

for i = 1, string.len(str) do
    print( string.sub(str, i, i) )
end

Abhängig von der Aufgabe in der Hand könnte es einfacher sein string.byte . Es ist auch die schnellsten Wege, weil es für neuen Teil vermeidet schaffen, die zu Hashing jeden neuen Zeichenfolge in Lua dank seine ziemlich teuer happends und prüfen, ob es bereits bekannt ist. Sie können Code von Symbolen im Voraus berechnen Sie mit dem gleichen string.byte suchen Lesbarkeit und Portabilität zu erhalten.

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

Es gibt bereits viele gute Ansätze in den bereitgestellten Antworten ( hier , here und hier ). Wenn die Geschwindigkeit ist, was sind Sie auf in erster Linie suchen, sollten Sie auf jeden Fall überlegen den Job durch Lua C-API zu tun, die ein Vielfaches schneller als rohe Lua-Code ist. Wenn mit vorgespannten chunks arbeiten (z. Lastfunktion ), der Unterschied ist nicht so groß, aber immer noch beträchtlich.

Wie für die rein Lua Lösungen, lassen Sie mich diese kleine Benchmark teilen, habe ich gemacht. Es deckt alle vorgesehene Antwort auf dieses Datum und fügt ein paar Optimierungen. Noch auf die grundlegende Sache zu betrachten ist:

Wie oft müssen Sie über Zeichen in String iterieren?

  • Wenn die Antwort „einmal“, als Sie zum ersten Mal Teil des banchmark aussehen sollten ( „raw speed“).
  • Ansonsten liefert der zweite Teil eine genauere Schätzung, weil es die Zeichenfolge in der Tabelle analysiert, die viel schneller zu durchlaufen. Sie sollten auch eine einfache Funktion für diese betrachten das Schreiben, wie @Jarriz vorgeschlagen.

Hier ist voll Code:

-- 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))

Beispiel Ausgabe (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

Ergebnis:

In meinem Fall die string.byte und string.sub waren am schnellsten in Bezug auf die reine Geschwindigkeit. Wenn Cache-Tabelle verwenden und es 10-mal pro Schleife wiederverwendet, die string.byte Version war am schnellsten selbst wenn Zeichencodes zurück zu Zeichen Umwandlung (was nicht immer notwendig ist, und hängt von der Nutzung).

Wie Sie wahrscheinlich bemerkt haben, habe ich einige Annahmen auf Grund meiner früheren Benchmarks und wandte sie auf den Code:

  1. Bibliothek Funktionen immer lokalisieren, wenn innerhalb von Schleifen verwendet werden sollen, weil es viel schneller ist.
  2. Einfügen von neuen Elementen in lua Tabelle ist viel schneller als tbl[idx] = value table.insert(tbl, value) verwenden.
  3. Looping durch Tabelle for i = 1, #tbl verwendet, ist ein wenig schneller als for k, v in pairs(tbl).
  4. bevorzugen immer die Version mit weniger Funktionsaufrufe, weil der Anruf selbst ein wenig auf die Ausführungszeit addiert.

Hoffe, es hilft.

Alle Menschen deuten auf eine weniger optimale Methode

wird am besten:

    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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top