Bagaimana cara mengulang karakter individu dalam string Lua?


93

Saya memiliki string di Lua dan ingin mengulang karakter individu di dalamnya. Tetapi tidak ada kode yang saya coba berfungsi dan manual resmi hanya menunjukkan cara menemukan dan mengganti substring :(

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

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

Jawaban:


131

Dalam lua 5.1, Anda dapat mengulang karakter string ini dengan beberapa cara.

Loop dasarnya adalah:

untuk i = 1, #str do
    lokal c = str: sub (i, i)
    - melakukan sesuatu dengan c
akhir

Tetapi mungkin lebih efisien menggunakan pola dengan string.gmatch()untuk mendapatkan iterator atas karakter:

untuk c di str: gmatch "." melakukan
    - melakukan sesuatu dengan c
akhir

Atau bahkan digunakan string.gsub()untuk memanggil fungsi untuk setiap karakter:

str: gsub (".", function (c)
    - melakukan sesuatu dengan c
akhir)

Dalam semua hal di atas, saya telah memanfaatkan fakta bahwa stringmodul disetel sebagai metatabel untuk semua nilai string, sehingga fungsinya dapat disebut sebagai anggota menggunakan :notasi. Saya juga menggunakan (baru 5.1, IIRC) #untuk mendapatkan panjang string.

Jawaban terbaik untuk aplikasi Anda bergantung pada banyak faktor, dan tolok ukur adalah teman Anda jika kinerja itu penting.

Anda mungkin ingin mengevaluasi mengapa Anda perlu mengulang karakter, dan untuk melihat salah satu modul ekspresi reguler yang telah terikat ke Lua, atau untuk pendekatan modern melihat ke modul lpeg Roberto yang mengimplementasikan Parsing Expression Grammers untuk Lua.


Terima kasih. Tentang modul lpeg yang telah Anda sebutkan - apakah itu menyimpan posisi token dalam teks asli setelah tokenisasi? Tugas yang perlu saya lakukan adalah menyorot sintaks bahasa sederhana tertentu di scite melalui lua (tanpa parser c ++ yang dikompilasi). Juga, bagaimana cara menginstal lpeg? Tampaknya ia memiliki sumber .c dalam distribusi - apakah ia perlu dikompilasi bersama lua?
grigoryvp

Membangun lpeg akan menghasilkan DLL (atau .so) yang harus disimpan di tempat yang membutuhkan dapat menemukannya. (yaitu di suatu tempat yang diidentifikasi oleh konten global package.cpath di instalasi lua Anda.) Anda juga perlu menginstal modul pendampingnya re.lua jika Anda ingin menggunakan sintaks yang disederhanakan. Dari tata bahasa lpeg, Anda bisa mendapatkan panggilan balik dan menangkap teks dengan berbagai cara, dan tentu saja mungkin menggunakan tangkapan untuk menyimpan lokasi kecocokan untuk digunakan nanti. Jika sorotan sintaks adalah tujuannya, maka PEG bukanlah pilihan alat yang buruk.
RBerteig

3
Belum lagi rilis terbaru SciTE (sejak 2.22) termasuk Scintillua, lexer berbasis LPEG, yang berarti dapat bekerja langsung di luar kotak, tidak perlu kompilasi ulang.
Stuart P. Bentley

11

Jika Anda menggunakan Lua 5, coba:

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

9

Bergantung pada tugas yang ada, ini mungkin lebih mudah digunakan string.byte. Ini juga merupakan cara tercepat karena menghindari pembuatan substring baru yang terjadi menjadi sangat mahal di Lua berkat hashing dari setiap string baru dan memeriksa apakah sudah dikenal. Anda dapat menghitung kode simbol yang Anda cari sebelumnya untuk string.bytemenjaga keterbacaan dan portabilitas.

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

Sudah ada banyak pendekatan yang baik dalam jawaban yang diberikan (di sini , di sini dan di sini ). Jika kecepatan adalah apa yang Anda terutama mencari, Anda pasti harus mempertimbangkan melakukan pekerjaan melalui Lua C API, yang banyak kali lebih cepat daripada kode Lua mentah. Saat bekerja dengan potongan yang dimuat sebelumnya (mis. Fungsi beban ), perbedaannya tidak terlalu besar, tetapi masih cukup besar.

Adapun solusi Lua murni , izinkan saya membagikan patokan kecil ini, yang telah saya buat. Ini mencakup setiap jawaban yang diberikan hingga tanggal ini dan menambahkan beberapa pengoptimalan. Namun, hal dasar yang perlu dipertimbangkan adalah:

Berapa kali Anda perlu mengulang karakter dalam string?

  • Jika jawabannya "sekali", maka Anda harus mencari bagian pertama dari banchmark ("kecepatan mentah").
  • Jika tidak, bagian kedua akan memberikan estimasi yang lebih tepat, karena mengurai string ke dalam tabel, yang jauh lebih cepat untuk diiterasi. Anda juga harus mempertimbangkan untuk menulis fungsi sederhana untuk ini, seperti saran @Jarriz.

Ini kode lengkapnya:

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

Contoh keluaran (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

Hasil:

Dalam kasus saya, string.bytedan string.subtercepat dalam hal kecepatan mentah. Saat menggunakan tabel cache dan menggunakannya kembali 10 kali per loop, string.byteversinya adalah yang tercepat bahkan saat mengonversi kode karakter kembali ke karakter (yang tidak selalu diperlukan dan bergantung pada penggunaan).

Seperti yang mungkin Anda perhatikan, saya telah membuat beberapa asumsi berdasarkan tolok ukur saya sebelumnya dan menerapkannya ke kode:

  1. Fungsi perpustakaan harus selalu dilokalkan jika digunakan di dalam loop, karena jauh lebih cepat.
  2. Memasukkan elemen baru ke dalam tabel lua jauh lebih cepat menggunakan tbl[idx] = valuedaripada table.insert(tbl, value).
  3. Perulangan melalui tabel menggunakan for i = 1, #tblsedikit lebih cepat dari for k, v in pairs(tbl).
  4. Selalu lebih suka versi dengan lebih sedikit pemanggilan fungsi, karena pemanggilan itu sendiri menambah sedikit waktu eksekusi.

Semoga membantu.


0

Semua orang menyarankan metode yang kurang optimal

Akan menjadi yang terbaik:

    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

"Kurang optimal" untuk tugas apa? "Terbaik" untuk tugas apa?
Oleg V. Volkov

0

Iterasi untuk membuat string dan mengembalikan string ini sebagai tabel dengan 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)

Mengeluarkan...

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.