Lua dizesindeki tek tek karakterler nasıl yinelenir?


93

Lua'da bir dizem var ve içindeki karakterleri tek tek yinelemek istiyorum. Ancak denediğim hiçbir kod işe yaramıyor ve resmi kılavuz yalnızca alt dizelerin nasıl bulunacağını ve değiştirileceğini gösteriyor :(

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

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

Yanıtlar:


131

Lua 5.1'de, bir dizgenin karakterlerini birkaç yolla yineleyebilirsiniz.

Temel döngü şöyle olacaktır:

i = 1 için #str do
    yerel c = str: sub (i, i)
    - c ile bir şeyler yap
son

Ancak string.gmatch(), karakterlerin üzerinde bir yineleyici elde etmek için bir desen kullanmak daha verimli olabilir :

c in str: gmatch için "." yapmak
    - c ile bir şeyler yap
son

Veya string.gsub()her karakter için bir işlev çağırmak için kullanmak için:

str: gsub (".", işlev (c)
    - c ile bir şeyler yap
son)

Yukarıdakilerin hepsinde, stringmodülün tüm dize değerleri için bir metatable olarak ayarlanması gerçeğinden faydalandım , böylece işlevleri :gösterimi kullanarak üyeler olarak çağrılabilir . Ayrıca #dize uzunluğunu elde etmek için (5.1, IIRC'ye yeni) kullandım.

Uygulamanız için en iyi cevap birçok faktöre bağlıdır ve performans önemliyse kıyaslamalar arkadaşınızdır.

Neden karakterler üzerinde yinelemeniz gerektiğini değerlendirmek ve Lua'ya bağlı normal ifade modüllerinden birine bakmak veya modern bir yaklaşım için Roberto'nun Lua için Ayrıştırma İfade Gramerlerini uygulayan lpeg modülüne bakın .


Teşekkürler. Bahsettiğiniz lpeg modülü hakkında - belirteçlerin konumlarını belirtme işleminden sonra orijinal metinde kaydediyor mu? Yapmam gereken görev, lua aracılığıyla (derlenmiş c ++ ayrıştırıcısı olmadan) scite'de belirli basit dili vurgulamaktır. Ayrıca, lpeg nasıl kurulur? Dağıtımda .c kaynağına sahip gibi görünüyor - lua ile birlikte derlenmesi gerekiyor mu?
grigoryvp

Bina lpeg, ihtiyaç duyulan yerde depolanması gereken bir DLL (veya .so) üretir. (yani, lua kurulumunuzdaki global package.cpath içeriğiyle tanımlanan bir yerde.) Basitleştirilmiş sözdizimini kullanmak istiyorsanız, refakatçi modülü re.lua'yı da kurmanız gerekir. Bir lpeg dilbilgisinden, geri aramaları alabilir ve birkaç yolla metin yakalayabilirsiniz ve daha sonra kullanmak üzere sadece eşleşmenin yerini saklamak için yakalamaları kullanmak kesinlikle mümkündür. Sözdizimi vurgusu amaçsa, PEG kötü bir araç seçimi değildir.
RBerteig

3
SciTE'nin en son sürümlerinden bahsetmiyorum bile (2.22'den beri), LPEG tabanlı bir lexer olan Scintillua'yı içerir, yani kutudan çıkar çıkmaz çalışabilir, yeniden derlemeye gerek yoktur.
Stuart P. Bentley

11

Lua 5 kullanıyorsanız şunu deneyin:

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

9

Eldeki göreve bağlı olarak kullanımı daha kolay olabilir string.byte. Aynı zamanda en hızlı yoldur çünkü Lua'da her yeni dizgeye hashing uygulanması ve zaten bilinip bilinmediğini kontrol etme sayesinde oldukça pahalı olan yeni alt dizeler oluşturmayı önler. string.byteOkunabilirliği ve taşınabilirliği korumak için aradığınız sembollerin kodunu önceden hesaplayabilirsiniz .

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

7

Verilen cevaplarda zaten birçok iyi yaklaşım var ( burada , burada ve burada ). Öncelikle aradığınız şey hız ise , işi kesinlikle ham Lua kodundan çok daha hızlı olan Lua'nın C API'si aracılığıyla yapmayı düşünmelisiniz. Önceden yüklenmiş parçalarla çalışırken (örn. Yükleme işlevi ), fark o kadar büyük değil, ancak yine de önemli.

Saf Lua çözümlerine gelince , yaptığım bu küçük ölçütü paylaşmama izin verin. Bu tarihe verilen her cevabı kapsar ve birkaç optimizasyon ekler. Yine de dikkate alınması gereken temel şey şudur:

Dizideki karakterleri kaç kez yinelemeniz gerekecek?

  • Cevap "bir kez" ise, banchmark'ın ilk kısmına bakmanız gerekir ("ham hız").
  • Aksi takdirde, ikinci bölüm daha kesin bir tahmin sağlayacaktır, çünkü dizeyi tabloya ayrıştırır, bu da yinelemesi çok daha hızlıdır. Bunun için @ Jarriz'in önerdiği gibi basit bir işlev yazmayı da düşünmelisiniz.

İşte tam 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))

Örnek çıktı (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

Sonuç:

Benim durumumda, string.byteve string.subham hız açısından en hızlıydı. Önbellek tablosunu kullanırken ve döngü başına 10 kez yeniden kullanırken, string.bytesürüm, karakter kodlarını karakterlere geri dönüştürürken bile en hızlıydı (bu her zaman gerekli değildir ve kullanıma bağlıdır).

Muhtemelen fark ettiğiniz gibi, önceki kıyaslamalarıma dayanarak bazı varsayımlar yaptım ve bunları koda uyguladım:

  1. Kütüphane fonksiyonları döngülerin içinde kullanılıyorsa her zaman yerelleştirilmelidir, çünkü çok daha hızlıdır.
  2. Lua tabloya yeni element takılması çok daha hızlı kullanıyor tbl[idx] = valuedaha table.insert(tbl, value).
  3. Tablo kullanarak döngü yapmak for i = 1, #tblbiraz daha hızlıdır for k, v in pairs(tbl).
  4. Her zaman daha az işlev çağrısı içeren sürümü tercih edin, çünkü çağrının kendisi yürütme süresine biraz ekler.

Umarım yardımcı olur.


0

Tüm insanlar daha az optimal bir yöntem önerir

En iyisi olacak:

    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

Hangi görev için "daha az optimal"? Hangi görev için "en iyisi"?
Oleg V. Volkov

0

Bir dizge oluşturmayı yinelemek ve bu dizeyi load () ile bir tablo olarak döndürmek ...

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)

Dışarı koyar...

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.