サーチ…
構文
- ipairs(numeric_table) - 数値インデックスiteratorを持つLuaテーブル
- ペア(input_table) - 汎用Luaテーブルイテレータ
- キー、値=次(input_table、input_key) - Luaテーブル値セレクタ
- table.insert(input_table、[position]、value) - 指定された値を入力テーブルに挿入する
- removed_value = table.remove(input_table、[position]) - positionで指定された値の最後または削除をポップします。
備考
テーブルはLuaで利用できる唯一のビルトインデータ構造です。これは見た目に応じて、エレガントでシンプルか混乱しています。
Luaテーブルは、キーが一意であり、キーも値もnil
はないキーと値のペアの集合です。そのため、Luaテーブルは他の言語の辞書、ハッシュマップ、または連想配列に似ています。スタック、キュー、セット、リスト、グラフなど、多くの構造パターンをテーブルで構築できます。最後に、テーブルを使用してLuaでクラスを構築し、 モジュールシステムを作成することができます 。
Luaはテーブルの使用方法に関する特別なルールを強制しません。テーブルに含まれる項目は、Luaの種類を混在させることができます。したがって、たとえば、1つのテーブルに文字列、関数、ブール値、数値、 さらには値またはキーとしての他のテーブルを含めることができます。
1で始まる連続した正の整数キーを持つLuaテーブルは、シーケンスを持つと言われます。正の整数キーを持つキーと値のペアはシーケンスの要素です。他の言語は、これを1ベースの配列と呼びます。特定の標準的な演算と関数は、テーブルのシーケンスに対してのみ動作し、シーケンスによってはテーブルに適用されたときに非決定的な動作をします。
テーブルの値をnil
に設定すると、テーブルからその値が削除されます。イテレーターは関連するキーを見ることができません。シーケンスを持つテーブルをコーディングするときは、シーケンスを分割しないようにすることが重要です。最後の要素を削除するか、要素を下に移動してギャップを閉じる標準のtable.remove
ような関数を使用してください。
テーブルの作成
空のテーブルを作成するのは次のように簡単です:
local empty_table = {}
また、単純な配列の形式で表を作成することもできます。
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
デフォルトでは、テーブルインデックスは1から始まることに注意してください。
また、関連要素を持つ表を作成することも可能です。
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
上記の使用法は、以下の構文の砂糖です。このインスタンスのキーは、タイプstringです。テーブルをレコードとして表示するために、上記の構文が追加されました。このレコードスタイルの構文は、 '基本的な使用法'のチュートリアルに示すように、文字列キーを使用してテーブルをインデックスする構文と並行しています。
備考のセクションで説明したように、レコードスタイルの構文はすべての可能なキーで機能しません。さらに、キーは任意の型の任意の値にすることができ、前の例では文字列と連続番号のみを扱いました。他の場合は、明示的な構文を使用する必要があります:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
テーブルの反復
Lua標準ライブラリは、テーブルのキーと値を反復するpairs
関数を提供します。 pairs
で反復するときは、テーブルのキーが数値であっても 、走査の順序は指定されていません。
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
数値キーを使用するテーブルの場合、Luaはipairs
関数を提供します。 ipairs
関数は、最初のnil
値が見つかるまで、常にtable[1]
、 table[2]
などから反復しtable[1]
。
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
ipairs()
を使用した反復処理は、ほんの少しでも必要な場合は機能しないことに注意してください。
input_table
は「穴」があります。 (詳細は、「配列として使用されるテーブルのギャップを避ける」を参照してください)。たとえば、次のようになります。table_with_holes = {[1] = "value_1", [3] = "value_3"}
キーがすべて数字ではありませんでした。例えば:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
もちろん、適切なシーケンスであるテーブルに対しても、次のことも有効です。
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
逆の順序で数値表を反復するのは簡単です:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
テーブルを反復処理する最後の方法は、 汎用for
ループの next
セレクタを使用することです。 pairs
ように、トラバーサルの指定順序はありません。 ( pairs
方法は、使用してnext
ので使用。内部的にnext
本質的により手動バージョンですpairs
。を参照してくださいpairs
のLuaのリファレンスマニュアルでとnext
のLuaのリファレンスマニュアルの詳細については。)
for key, value in next, input_table do
print(key, value)
end
基本的な使用法
テーブルの基本的な使い方には、テーブル要素へのアクセスと割り当て、テーブルコンテンツの追加、およびテーブルコンテンツの削除が含まれます。これらの例は、テーブルの作成方法を理解していることを前提としています
要素へのアクセス
次の表を見ると、
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
索引構文を使用して表の順次部分を索引付けすることができます。索引構文の引数は、目的のキー/値ペアのキーです。作成チュートリアルで説明したように、宣言構文のほとんどは、キーと値のペアを宣言するための構文的な砂糖です。シーケンシャルに含まれる要素は、 example_table
最初の5つの値と同様に、増加する整数値をキーとして使用します。レコード構文はフィールドの名前を文字列として使用します。
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
文字列キーの場合、テーブル作成時に文字列キーのレコードスタイルの構文と平行する構文糖があります。次の2行は同等です。
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
これまで使用していなかったキーを使用してテーブルにアクセスできます。これは他の言語と同じようにエラーではありません。そうすると、デフォルト値nil
が返されます。
要素の割り当て
既存のテーブル要素を変更するには、インデックス構文を使用してテーブルに割り当てます。さらに、レコードスタイルの索引付け構文を使用して値を設定することもできます
example_table.cure = "Lots of water, the toilet, and time"
print(example_table.cure) --> Lots of water, the toilet, and time
example_table[2] = "Constipation"
print(example_table[2]) --> Constipation
代入を使用して、既存のテーブルに新しい要素を追加することもできます。
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
特別な注意:一部の文字列はレコード構文ではサポートされていません。詳細は備考欄を参照してください。
要素の削除
前述のように、値が割り当てられていないキーのデフォルト値はnil
です。テーブルから要素を削除するのは、キーの値をデフォルト値に戻すのと同じくらい簡単です。
example_table[100] = "Face Mask"
要素は、未設定の要素と区別できなくなりました。
テーブルの長さ
表は単に連想配列(注釈を参照)ですが、1から始まる連続した整数キーが使用される場合、表はシーケンスを持つと言われます 。
テーブルのシーケンス部分の長さを見つけるには#
を使います:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
長さ操作を使用すると、アイテムを簡単にシーケンステーブルに追加できます。
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
上記の例では、以前の値#example_table
ある8
加算、 1
あなたのシーケンス内の次の有効な整数キー与える9
...のでexample_table[9] = 'a'
。これはテーブルの任意の長さで動作します。
特記事項:連続しておらず、1から始まる整数キーを使用すると、シーケンスを疎テーブルにするシーケンスが壊れます 。その場合、長さ演算の結果は未定義です。備考欄を参照してください。
表ライブラリ関数を使用した要素の追加と削除
テーブルに要素を追加する別の方法は、 table.insert()
関数です。挿入機能はシーケンス表に対してのみ機能します。関数を呼び出すには2つの方法があります。最初の例は最初の使用法を示しています。ここでは、要素を挿入するインデックス(2番目の引数)を指定します。これにより、指定されたインデックスのすべての要素が#table
1つの位置に#table
されます。 2番目の例は、インデックスが指定されておらず、指定された値がテーブルの末尾(index #table + 1
)に追加されている、 table.insert()
他の使い方を示しています。
local t = {"a", "b", "d", "e"}
table.insert(t, 3, "c") --> t = {"a", "b", "c", "d", "e"}
t = {"a", "b", "c", "d"}
table.insert(t, "e") --> t = {"a", "b", "c", "d", "e"}
要素を削除するためにtable.insert()
を並列table.insert()
するには、 table.remove()
ます。同様に、2つの呼び出しセマンティクスを持ちます.1つは、指定された位置で要素を削除するメソッドと、もう1つはシーケンスの最後から削除する関数です。シーケンスの途中から削除する場合、次の要素はすべて1つのインデックスに移動します。
local t = {"a", "b", "c", "d", "e"}
local r = table.remove(t, 3) --> t = {"a", "b", "d", "e"}, r = "c"
t = {"a", "b", "c", "d", "e"}
r = table.remove(t) --> t = {"a", "b", "c", "d"}, r = "e"
これらの2つの関数は、指定されたテーブルを変更します。 table.insert()
とtable.remove()
を呼び出す2番目の方法を伝えることができるように、テーブルにスタックセマンティクスを提供します。これを利用して、以下の例のようなコードを書くことができます。
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
おそらく非効率的にFisher-Yates Shuffleを実装します。これは、使用table.insert()
同じテーブルの端、及び上にランダムに抽出された要素を追加するtable.remove()
ランダムテーブルの残りunshuffled部分から要素を抽出します。
配列として使用されるテーブルの隙間を避ける
条件を定義する
配列では、シーケンスとして使用されるLuaテーブルを意味します。例えば:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
この表をシーケンスとして使用しています:整数でキーを付けられた項目のグループ。多くの言語がこれを配列と呼びます。しかし厳密に言えば、Luaには配列のようなものはありません。テーブルはいくつかあり、その中には配列のようなものがあり、その中にはハッシュのようなもの(または辞書のようなもの)があり、その中のいくつかは混在しています。
私たちのpets
配列についての重要な点は、ギャップがないことです。最初の項目、 pets[1]
は文字列 "dogs"、2番目の項目、 pets[2]
は文字列 "cats"、最後の項目pets[3]
は "birds"です。 Luaの標準ライブラリとLua用に書かれたほとんどのモジュールは、シーケンスの最初のインデックスとして1を仮定します。したがって、ギャップレス配列には、シーケンス内の数字を1 1..n
も失わずに1..n
項目があります。 (制限的なケースでは、 n = 1
であり、配列には項目が1つしかありません)。
Luaは組み込み関数ipairs
を提供して、そのようなテーブルを反復処理します。
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
これは、 "位置1のアイテムは犬です。"、 "位置2のアイテムは猫です"、 "位置3のアイテムは鳥です"と表示されます。
しかし、私たちが次のことをすればどうなりますか?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
この2番目の例のような配列は疎配列です。シーケンスにはギャップがあります。この配列は次のようになります。
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
nil値は任意のメモリを占有しません。内部的にluaは値[1] = "dogs"
、 [2] = "cats"
、 [3] = "birtds"
、 [12] = "goldfish"
すぐに質問に答えるために、鳥は鳥の後でipairs
でしょう。私たちのコードを調整しない限り、 pets[12]
「金魚」には決して届きません。これは、 ipairs
が1..n-1
から反復するためipairs
n
は見つかった最初のnil
位置です。 Luaはtable[length-of-table + 1]
をnil
定義していtable[length-of-table + 1]
。したがって、適切な順序で、Luaが3つのアイテムの配列の4番目のアイテムを取得しようとすると、繰り返しが停止します。
いつ?
スパース配列で問題が発生する最も一般的な2つの場所は、(i)配列の長さを決定しようとするとき、(ii)配列を反復しようとするときです。特に:
- 使用する場合
#
長さ演算子長さ演算子は、最初はカウントを停止するのでnil
を発見しました。 - 上で述べたように、
ipairs()
関数を使うと、見つかった最初のnil
反復を止めます。 - このメソッドは、最初の
nil
見つかったときに展開を停止するため、table.unpack()
関数を使用しています。 - 上記のいずれかに(直接的または間接的に)アクセスする他の機能を使用する場合。
この問題を回避するには、テーブルを配列にすると予想される場合は、ギャップを導入しないようにコードを記述することが重要です。ギャップはいくつかの方法で導入することができます:
- 配列に間違った位置に何かを追加した場合。
- 配列に
nil
値を挿入した場合。 - 配列から値を削除する場合。
あなたは、「しかし、私は決してそのようなことはしません」と考えるかもしれません。うーん、意図的にではありませんが、ここでは物事がどのように悪くなるかの具体例があります。 Rubyのselect
とPerlのgrep
のようなLua用のフィルタメソッドを記述したいとします。このメソッドは、テスト関数と配列を受け入れます。それは配列を繰り返し処理し、各項目に対してテストメソッドを順番に呼び出します。項目が合格すると、その項目はメソッドの最後に返される結果配列に追加されます。以下はバグの実装です:
local filter = function (fun, t)
local res = {}
for idx, item in ipairs(t) do
if fun(item) then
res[idx] = item
end
end
return res
end
問題は、関数がfalse
を返すとき、シーケンス内の数値をスキップすることです。 filter(isodd, {1,2,3,4,5,6,7,8,9,10})
呼び出すと想像してください。 filter
渡された配列に偶数があるたびに、返されるテーブルにギャップがありfilter
。
ここには固定実装があります:
local filter = function (fun, t)
local res = {}
for _, item in ipairs(t) do
if fun(item) then
res[#res + 1] = item
end
end
return res
end
ヒント
- 標準関数を使用する:
table.insert(<table>, <value>)
常に配列の末尾に追加されます。table[#table + 1] = value
はこれのための短い手です。table.remove(<table>, <index>)
はギャップを埋めるために次の値をすべて戻します(遅くすることもできます)。 - チェック
nil
ようなもの避け、挿入する前に値table.pack(function_call())
こっそりかもしれませんが、nil
私たちのテーブルに値を。 - 挿入後に
nil
値をチェックし、必要であれば、連続するすべての値をシフトしてギャップを埋める。 - 可能であれば、プレースホルダー値を使用します。たとえば、
0
またはその他のプレースホルダー値をnil
に変更します。 - 隙間を余儀なくされることがやむを得ない場合、これは徹底的に文書化(コメント)されるべきである。
-
__len()
、#
演算子を使用します。
6の例:
tab = {"john", "sansa", "daenerys", [10] = "the imp"}
print(#tab) --> prints 3
setmetatable(tab, {__len = function() return 10 end})
-- __len needs to be a function, otherwise it could just be 10
print(#tab) --> prints 10
for i=1, #tab do print(i, tab[i]) end
--> prints:
-- 1 john
-- 2 sansa
-- 3 daenerys
-- 4 nil
-- ...
-- 10 the imp
for key, value in ipairs(tab) do print(key, value) end
--> this only prints '1 john \n 2 sansa \n 3 daenerys'
もう1つの方法は、 pairs()
関数を使用して非整数インデックスをフィルタリングすることです。
for key in pairs(tab) do
if type(key) == "number" then
print(key, tab[key]
end
end
-- note: this does not remove float indices
-- does not iterate in order