途中までですが、まとめてみました。ありがとうございます。
以下、検証してみてください。
OneCompilerのLuaオンラインエディタは標準入力をサポートしており、ユーザーはI/Oタブの「STDIN」テキストボックスを使用してプログラムに入力を与えることができます。
----------------------------------------------------
-- 3. Table
----------------------------------------------------
-- nil以外の任意の値をキーとして使用:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- "tau" を出力
-- 数値と文字列のキーは値で一致するが、
-- tableは識別子で一致する。
a = u['@!#'] -- 現在 a = 'qbert'.
b = u[{}] -- 1729を期待するかもしれないが、nilが得られる:
-- b = nil、なぜなら見つからないから。
-- 見つからないのは、使用したキーがデータ保存時に使ったものと
-- 同じオブジェクトではないため。
-- したがって文字列と数値の方が移植性の高いキー。
-- table引数1つだけの関数呼び出しには括弧が不要:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- 'Sonmi~451'を出力。
print()
print("uの中身")
for key, val in pairs(u) do -- Tableを走査
print(key, val)
end
print()
print("_G は特殊なtableで、すべてのグローバル変数を保持する")
print(_G['_G'] == _G) -- 'true'を出力。
print()
-- リスト/配列としての使用
print("リストリテラルは暗黙的に整数キーを追加:")
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v はリストのサイズ
print(v[i]) -- インデックスは 1 から始まる!! クレイジーだ!
end
-- 'list'は本当の型ではなく、v は実際にはtableで、
-- 連続した整数をキーとして使用しているだけで、リストのように使える。
Output:
tau
Sonmi~451
uの中身
table: 0x55e2b265bda0 1729
@!# qbert
6.28 tau
_G は特殊なtableで、すべてのグローバル変数を保持する
true
リストリテラルは暗黙的に整数キーを追加:
value1
value2
1.21
gigawatts
どうしてbはnilになるのですか。
-- nil以外の任意の値をキーとして使用:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- "tau" を出力
print(u['@!#'])
print(u[{}])
-- 数値と文字列のキーは値で一致するが、
-- tableは識別子で一致する。
a = u['@!#'] -- 現在 a = 'qbert'.
b = u[{}] -- 1729を期待するかもしれないが、nilが得られる:
-- b = nil、なぜなら見つからないから。
-- 見つからないのは、使用したキーがデータ保存時に使ったものと
-- 同じオブジェクトではないため。
-- したがって文字列と数値の方が移植性の高いキー。
print(a) --'qbert'
print(b) --nil
テーブルをキーにした場合の動作
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
b = u[{}] -- nil になる
理由:テーブルは**参照(identity)**で比較される
-- テーブル定義時
local key1 = {} -- 新しいテーブルオブジェクトを作成
u = {[key1] = 1729} -- このkey1への参照で保存
-- アクセス時
local key2 = {} -- 別の新しいテーブルオブジェクトを作成
print(u[key2]) -- nil (key2 ≠ key1)
print(key1 == key2) -- false (異なるオブジェクト)
値による比較 vs 参照による比較
数値と文字列:値で比較
-- 数値キー
u[6.28] = 'tau'
print(u[6.28]) -- 'tau' (値が同じなので見つかる)
local x = 6.28
print(u[x]) -- 'tau' (xの値が6.28なので見つかる)
-- 文字列キー
u['@!#'] = 'qbert'
print(u['@!#']) -- 'qbert' (値が同じなので見つかる)
local s = '@!#'
print(u[s]) -- 'qbert' (sの値が'@!#'なので見つかる)
テーブル:**参照(アイデンティティ)**で比較
-- テーブルキー
local t1 = {}
local t2 = {}
u[t1] = 1729
print(u[t1]) -- 1729 (同じオブジェクトなので見つかる)
print(u[t2]) -- nil (異なるオブジェクトなので見つからない)
print(t1 == t2) -- false (内容が空で同じでも、別オブジェクト)
詳しい例
-- 例1: テーブルを変数に保存して使う
local key_table = {x = 1}
local data = {}
data[key_table] = "found!"
print(data[key_table]) -- "found!" (同じ参照を使用)
local another = {x = 1} -- 内容は同じだが別オブジェクト
print(data[another]) -- nil (別の参照)
-- 例2: 同じ変数なら何度でもアクセス可能
local my_key = {}
data[my_key] = 100
print(data[my_key]) -- 100
print(data[my_key]) -- 100 (何度でもOK)
-- 例3: 新しい{}を作るたびに別オブジェクト
print(data[{}]) -- nil
print(data[{}]) -- nil (毎回新しいテーブルが作られる)
メモリ上のイメージ
-- メモリ上で何が起きているか
-- 定義時:
local t1 = {} -- メモリアドレス: 0x1000
u = {[t1] = 1729} -- キー: 0x1000, 値: 1729
-- アクセス時:
local t2 = {} -- メモリアドレス: 0x2000 (別の場所)
print(u[t2]) -- キー 0x2000 を探すが見つからない → nil
-- 同じ変数を使う:
print(u[t1]) -- キー 0x1000 を探す → 見つかる → 1729
実用例:テーブルをキーとして使う正しい方法
-- オブジェクトに関連データを紐付ける
local player1 = {name = "Alice"}
local player2 = {name = "Bob"}
local scores = {}
scores[player1] = 100
scores[player2] = 200
print(scores[player1]) -- 100
print(scores[player2]) -- 200
-- 新しく作ったテーブルでは見つからない
print(scores[{name = "Alice"}]) -- nil
関数をキーにする場合も同様
local function f1() end
local function f2() end
local registry = {}
registry[f1] = "function 1"
registry[f2] = "function 2"
print(registry[f1]) -- "function 1"
print(registry[f2]) -- "function 2"
-- 別の関数(定義が同じでも)
local function f3() end
print(registry[f3]) -- nil
なぜ文字列と数値は値で比較されるのか
-- 文字列と数値は「インターン」される
-- (同じ値は同じメモリ位置を指す)
local s1 = "hello"
local s2 = "hello"
-- 内部的にs1とs2は同じメモリを指す可能性が高い
local n1 = 42
local n2 = 42
-- 数値も値として比較される
-- しかしテーブルは毎回新しいオブジェクト
local t1 = {}
local t2 = {}
-- t1とt2は確実に異なるメモリ位置
比較表
| キーの型 | 比較方法 | 同じ値で再作成 | 例 |
|---|---|---|---|
| 数値 | 値 | ○ 見つかる | u[6.28] |
| 文字列 | 値 | ○ 見つかる | u['@!#'] |
| テーブル | 参照 | × 見つからない | u[{}] |
| 関数 | 参照 | × 見つからない | u[function() end] |
| boolean | 値 | ○ 見つかる | u[true] |
実際のコード動作確認
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
-- 文字列キー:値で比較
a = u['@!#'] -- 'qbert' (見つかる)
print(a) -- qbert
-- テーブルキー:参照で比較
b = u[{}] -- nil (新しい{}は別オブジェクト)
print(b) -- nil
-- 正しく取得するには:
local key = {}
u[key] = 9999
print(u[key]) -- 9999 (同じ参照を使う)
まとめ
b = u[{}]がnilになる理由:
-
u = {[{}] = 1729}で保存した{}と -
b = u[{}]でアクセスする{}は - 別々のテーブルオブジェクト(メモリ上の別の場所)
- テーブルは**参照(アイデンティティ)**で比較される
- 同じ参照を使わないと見つからない →
nil
移植性の高いキー:
- 数値と文字列は値で比較されるので、同じ値なら必ず見つかる
- テーブルや関数は参照で比較されるので、同じ変数を保持する必要がある
以下は、Lua入門関連資料から抜粋参考にしました。ありがとうございます。
テーブルとは
Luaには、まとめてデータを扱う仕組みにテーブルと呼ばれるものを使います。
テーブルの実体は連想配列であり、添え字、つまりデータの場所を表すキーは文字列になります。
Luaには数字を添え字として扱う配列はなく、構造体などもありません。
あらゆるデータ構造をテーブルで表現します。
テーブルの作成
テーブルを作成するには以下のようにします。
table = {}
このようにすることでtableという名前の変数にテーブルが作成されます。
キーを文字列にする
テーブルはキーを文字列として指定することが出来ます。
テーブルに代入する値の型はそれぞれ違っていても構いません。
table["str"] = "Lua"
table["no"] = 1
table["bool"] = true
キーを数字にする
テーブルのキーには数字を使用することもでき、配列のように扱うことが出来ます。
table[1] = "Lua"
table[2] = 1
table[3] = true
このようにキーを指定して、値を代入します。
なお、Luaのテーブルを数字で指定する場合は 1 から指定するようにしましょう。
キーが 0 やマイナスの値を指定して、値を代入することは可能ですが、
Luaではテーブルのキーを 1 を前提にしているため注意が必要です
Luaのイテレータ
Luaにはイテレータ用の関数が用意されています。
テーブルのキーが文字列の場合は pairs関数、数字の場合はipairs関数を使用します。
pairs関数
文字列がキーの場合はpairs関数を使います。
t = {}
t["str1"] = "str1"
t["str2"] = "str2"
t["str3"] = "str3"
for i, ver in pairs(t) do
print(ver)
end
上記の例ではテーブル t に格納されている全ての値を表示します。
ただし、文字列をキーとするテーブルでは表示される順序が不定です。
もし、順序を一定にしたい場合は数字をキーとしてテーブルを作成するようにしましょう。
ipairs関数
数字がキーの場合はipairs関数を使います。
使い方はpairs関数と同じです。
t = {}
t[1] = "value1"
t[2] = "value2"
t[3] = "value3"
for i, ver in ipairs(t) do
print(ver)
end
前節でテーブルのキーは1からが前提になっていると説明しましたが、iparis関数もそのようになっています。
試しに以下のコードを実行してみてください。
t = {}
t[0] = "value0"
t[1] = "value1"
t[2] = "value2"
t[3] = "value3"
for i, ver in ipairs(t) do
print(ver)
end
Output:
value1
value2
value3
実行した結果では、value0 は表示されず、value1から表示されたはずです。
この結果からテーブルの添え字が1を前提としているということが分かると思います。
以下、indexが連続してないと止まります。
t = {}
t[0] = "value0"
t[1] = "value1"
t[2] = "value2"
t[4] = "value3" --[4]
for i, ver in ipairs(t) do
print(ver)
end
Output:
value1
value2