__newindex メタメソッド
CoddyのLuaジャーニー「ロジックとプログラムの流れ」セクションの一部。レッスン 19/54。
テーブルに存在しないキーを読み取ろうとしたときに何が起こるかを、__indexが制御する仕組みを学びました。次はその対となる__newindexについて見ていきましょう。これは、テーブルにまだ存在しないキーに値を代入したときに何が起こるかを制御します。
__newindexメタメソッドは、テーブルに現在存在しないキーに値を代入しようとするたびに呼び出されます。これにより、変更が行われる前にそれを捕捉して制御できます。
テーブルに新しいキーが追加されないようにする簡単な例を見てみましょう。
local readOnly = {}
local meta = {
__newindex = function(table, key, value)
print("Cannot add key: " .. key)
end
}
setmetatable(readOnly, meta)
readOnly.name = "Alice" -- 出力: Cannot add key: name
print(readOnly.name) -- 出力: nilreadOnly.name = "Alice"を設定しようとすると、Luaはテーブルにnameが存在しないことを認識します。Luaはそれを追加する代わりに、3つの引数(テーブル自身、設定するキー、代入する値)を指定して__newindex関数を呼び出します。
この例では、関数は単にメッセージを出力するだけで、実際には何も保存しません。
テーブルにデータを格納する前に検証するために、__newindexを使うこともできます。
local player = {}
local meta = {
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
rawset(t, key, value)
end
end
}
setmetatable(player, meta)
player.health = "high" -- 出力: Health must be a number!
player.health = 100 -- 許可され、rawset で保存される
player.health = "high" -- メッセージなし: health はすでに存在する2つ目の例でrawset()が使われていることに注目してください。この関数はメタテーブルを迂回し、テーブル内の値を直接設定します。
これがないと、値を設定する際に再び__newindexが呼び出され、無限ループが発生します。
__newindex はテーブルに存在しないキーに対してのみ実行されるため、このチェックは各キーへの最初の代入を保護します。rawset で health が保存されると、その後の代入では値が直接変更されます。すべての代入をチェックするには、テーブル自体を空のままにし、__index を通じて読み取る別のテーブルに値を保存します。
local data = {}
local player = setmetatable({}, {
__index = data,
__newindex = function(t, key, value)
if key == "health" and type(value) ~= "number" then
print("Health must be a number!")
else
data[key] = value
end
end
})
player.health = 100
player.health = "high" -- 出力: Health must be a number!
print(player.health) -- 出力: 100player自身のキーを持つことはないため、すべての代入は__newindexを経由し、すべての読み取りは__indexを通じてdataにフォールスルーします。
チャレンジ
簡単新しいキーに対して文字列値のみを受け入れるテーブルを返す関数 createValidatedTable を記述してください。この関数は key と value を受け取ります。
空のテーブルを作成し、値の型を検証する __newindex を持つメタテーブルを設定してください。文字列以外の値を追加しようとした場合は、rawset() を使って代わりに文字列 "invalid" を格納します。文字列値の場合は、rawset() を使ってそのまま格納します。
ロジック:
- 空のテーブルを作成する
- テーブル、キー、値を受け取る
__newindexを持つメタテーブルを作成する __newindex内で、type(value) == "string"を使って値が文字列かどうか確認する- 文字列の場合は、
rawset(t, key, value)を使って格納する - 文字列ではない場合は、
rawset(t, key, "invalid")を使って、代わりに"invalid"を格納する setmetatable()を使ってメタテーブルをテーブルに設定するrawset()を使って、初期のキーと値のペアをテーブルに追加する(メタテーブルを介さない)- テーブルを返す
パラメーター:
key(文字列): テーブルに追加する初期キーvalue(文字列): テーブルに追加する初期値
戻り値: 初期のキーと値のペアを含み、文字列値のみを受け入れる検証機能を備えたテーブル(table)
自分で試してみよう
function createValidatedTable(key, value)
-- ここにコードを書いてください
end
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
ロジックとプログラムの流れのすべてのレッスン
自分で練習してみよう: Luaオンラインコンパイラ