Luaに文字列があり、その中の個々の文字を反復処理したい。しかし、私が試したコードは機能せず、公式マニュアルは部分文字列を検索して置き換える方法のみを示しています:(
str = "abcd"
for char in str do -- error
print( char )
end
for i = 1, str:len() do
print( str[ i ] ) -- nil
end
回答:
Lua 5.1では、いくつかの方法で文字列の文字を反復できます。
基本的なループは次のようになります。
for i = 1、#str do ローカルc = str:sub(i、i) -cで何かをする 終わり
ただし、パターンを使用string.gmatch()
して文字のイテレータを取得する方が効率的です。
str:gmatch "のcの場合。" 行う -cで何かをする 終わり
またはstring.gsub()
、各文字の関数を呼び出すために使用することもできます。
str:gsub( "。"、function(c) -cで何かをする 終わり)
上記のすべてにおいて、string
モジュールがすべての文字列値のメタテーブルとして設定されているため、その関数を:
表記法を使用してメンバーとして呼び出すことができるという事実を利用しました。#
文字列の長さを取得するために(5.1の新機能、IIRC)も使用しました。
アプリケーションの最良の答えは多くの要因に依存し、パフォーマンスが重要になる場合、ベンチマークはあなたの友人です。
文字を反復処理する必要がある理由を評価し、Luaにバインドされている正規表現モジュールの1つを調べるか、最新のアプローチについては、Luaの解析式グラマーを実装するRobertoのlpegモジュールを調べます。
手元のタスクによっては、使いやすいかもしれませんstring.byte
。また、新しい文字列をハッシュして既知であるかどうかを確認することにより、Luaでかなり高価になる新しい部分文字列の作成を回避できるため、これも最速の方法です。string.byte
読みやすさと移植性を維持するために、同じように検索するシンボルのコードを事前に計算できます。
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
提供された回答には、すでに多くの優れたアプローチがあります(ここ、ここ、およびここ)。速度が主に求めているものである場合は、LuaのC APIを介してジョブを実行することを検討する必要があります。これは、Lua Luaコードより何倍も高速です。プリロードされたチャンクを使用する場合(例:ロード機能))を操作する場合、その違いはそれほど大きくありませんが、それでもかなりの違いがあります。
純粋な Luaソリューションこの小さなベンチマークを共有させてください。この日付に対するすべての提供された回答をカバーし、いくつかの最適化を追加します。それでも、考慮すべき基本的なことは次のとおりです。
文字列内の文字を何回繰り返す必要がありますか?
ここに完全なコードがあります:
-- 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))
出力例(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
結果:
私の場合、string.byte
とstring.sub
は生の速度の点で最速でした。キャッシュテーブルを使用してループごとに10回再利用する場合、string.byte
バージョンは文字コードを文字に戻すときにも最速でした(これは必ずしも必要ではなく、使用方法によって異なります)。
おそらくお気づきでしょうが、私は以前のベンチマークに基づいていくつかの仮定を行い、それらをコードに適用しました。
tbl[idx] = value
よりもはるかに高速ですtable.insert(tbl, value)
。for i = 1, #tbl
は、よりも少し高速ですfor k, v in pairs(tbl)
。それが役に立てば幸い。
すべての人が最適ではない方法を提案します
最高になります:
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