Làm thế nào để lặp lại các ký tự riêng lẻ trong chuỗi Lua?


88

Tôi có một chuỗi trong Lua và muốn lặp lại các ký tự riêng lẻ trong đó. Nhưng không có mã nào tôi đã thử hoạt động và hướng dẫn chính thức chỉ hiển thị cách tìm và thay thế các chuỗi con :(

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

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

Câu trả lời:


125

Trong lua 5.1, bạn có thể lặp lại các ký tự của chuỗi này theo một vài cách.

Vòng lặp cơ bản sẽ là:

cho i = 1, #str do
    cục bộ c = str: sub (i, i)
    - làm gì đó với c
kết thúc

Nhưng có thể hiệu quả hơn nếu sử dụng một mẫu string.gmatch()để lấy một trình lặp qua các ký tự:

cho c trong str: gmatch "." làm
    - làm gì đó với c
kết thúc

Hoặc thậm chí sử dụng string.gsub()để gọi một hàm cho mỗi ký tự:

str: gsub (".", function (c)
    - làm gì đó với c
kết thúc)

Trong tất cả những điều trên, tôi đã tận dụng thực tế là stringmô-đun được đặt như một mô-đun có thể đo lường được cho tất cả các giá trị chuỗi, vì vậy các hàm của nó có thể được gọi là thành viên bằng cách sử dụng :ký hiệu. Tôi cũng đã sử dụng (mới với 5.1, IIRC) #để lấy độ dài chuỗi.

Câu trả lời tốt nhất cho ứng dụng của bạn phụ thuộc vào rất nhiều yếu tố và điểm chuẩn là người bạn của bạn nếu hiệu suất trở nên quan trọng.

Bạn có thể muốn đánh giá lý do tại sao bạn cần phải lặp lại các ký tự và xem xét một trong các mô-đun biểu thức chính quy đã được liên kết với Lua hoặc để có cách tiếp cận hiện đại, hãy xem mô-đun lpeg của Roberto, mô-đun này thực hiện Phân tích cú pháp biểu thức Grammers cho Lua.


Cảm ơn. Về mô-đun lpeg mà bạn đã đề cập - nó có lưu vị trí mã thông báo trong văn bản gốc sau khi mã hóa không? Nhiệm vụ tôi cần thực hiện là đánh dấu cú pháp ngôn ngữ đơn giản cụ thể trong scite thông qua lua (không có trình phân tích cú pháp c ++ được biên dịch). Ngoài ra, làm thế nào để cài đặt lpeg? Có vẻ như nó có nguồn .c trong bản phân phối - nó có cần được biên dịch cùng với lua không?
grigoryvp

Xây dựng lpeg sẽ tạo ra một DLL (hoặc .so) cần được lưu trữ ở nơi yêu cầu có thể tìm thấy nó. (tức là một nơi nào đó được xác định bởi nội dung của global package.cpath trong cài đặt lua của bạn.) Bạn cũng cần cài đặt mô-đun đồng hành re.lua của nó nếu bạn muốn sử dụng cú pháp đơn giản của nó. Từ ngữ pháp lpeg, bạn có thể nhận lệnh gọi lại và chụp văn bản theo một số cách và chắc chắn có thể sử dụng lệnh chụp để lưu trữ vị trí khớp để sử dụng sau này. Nếu đánh dấu cú pháp là mục tiêu, thì PEG không phải là một lựa chọn công cụ tồi.
RBerteig

3
Chưa kể các bản phát hành mới nhất của SciTE (kể từ 2.22) bao gồm Scintillua, một lexer dựa trên LPEG, có nghĩa là nó có thể hoạt động ngay lập tức mà không cần biên dịch lại.
Stuart P. Bentley

11

Nếu bạn đang sử dụng Lua 5, hãy thử:

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

9

Tùy thuộc vào nhiệm vụ hiện tại, nó có thể dễ sử dụng hơn string.byte. Đây cũng là cách nhanh nhất vì nó tránh được việc tạo ra các chuỗi con mới có thể xảy ra khá tốn kém trong Lua nhờ băm từng chuỗi mới và kiểm tra xem nó đã được biết chưa. Bạn có thể tính toán trước mã các ký hiệu mà bạn tìm kiếm với cùng một mã string.byteđể duy trì khả năng đọc và tính di động.

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

5

Đã có rất nhiều cách tiếp cận tốt trong các câu trả lời được cung cấp ( đây , đâyđây ). Nếu tốc độ là thứ bạn chủ yếu tìm kiếm, bạn chắc chắn nên cân nhắc thực hiện công việc thông qua API C của Lua, nhanh hơn nhiều lần so với mã Lua thô. Khi làm việc với các khối được tải trước (ví dụ: hàm tải ), sự khác biệt không lớn lắm nhưng vẫn đáng kể.

Đối với các giải pháp Lua thuần túy , hãy để tôi chia sẻ điểm chuẩn nhỏ này, tôi đã thực hiện. Nó bao gồm mọi câu trả lời được cung cấp cho đến ngày này và thêm một số tối ưu hóa. Tuy nhiên, điều cơ bản cần xem xét là:

Bạn sẽ cần lặp lại bao nhiêu lần các ký tự trong chuỗi?

  • Nếu câu trả lời là "một lần", bạn nên tra cứu phần đầu tiên của dấu hiệu ("tốc độ thô").
  • Nếu không, phần thứ hai sẽ cung cấp ước tính chính xác hơn, vì nó phân tích cú pháp chuỗi vào bảng, việc lặp lại nhanh hơn nhiều. Bạn cũng nên cân nhắc viết một hàm đơn giản cho việc này, như @Jarriz đã đề xuất.

Đây là mã đầy đủ:

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

Đầu ra ví dụ (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

Kết quả:

Trong trường hợp của tôi, tốc độ thô string.bytestring.subnhanh nhất. Khi sử dụng bảng bộ nhớ cache và sử dụng lại nó 10 lần mỗi vòng lặp, string.bytephiên bản nhanh nhất ngay cả khi chuyển đổi mã ký tự trở lại ký tự (không phải lúc nào cũng cần thiết và tùy thuộc vào cách sử dụng).

Như bạn có thể đã nhận thấy, tôi đã đưa ra một số giả định dựa trên các điểm chuẩn trước đây của mình và áp dụng chúng cho mã:

  1. Các hàm thư viện nên luôn được bản địa hóa nếu được sử dụng bên trong các vòng lặp, vì nó nhanh hơn rất nhiều.
  2. Chèn phần tử mới vào bảng lua bằng cách sử dụng nhanh tbl[idx] = valuehơn nhiều table.insert(tbl, value).
  3. Sử dụng vòng lặp qua bảng for i = 1, #tblnhanh hơn một chút for k, v in pairs(tbl).
  4. Luôn thích phiên bản có ít lệnh gọi hàm hơn, vì bản thân lệnh gọi sẽ thêm một chút vào thời gian thực thi.

Hy vọng nó giúp.


0

Tất cả mọi người đề xuất một phương pháp kém tối ưu hơn

Sẽ tốt nhất:

    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

"Ít tối ưu hơn" cho nhiệm vụ gì? "Tốt nhất" cho nhiệm vụ gì?
Oleg V. Volkov

0

Lặp lại để tạo một chuỗi và trả về chuỗi này dưới dạng một bảng có 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)

Đặt ra ...

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.