Lua メタテーブル


Lua テーブルでは、対応するキーにアクセスして値を取得できますが、2 つのテーブルを操作することはできません。

そのため、Lua はテーブルの動作を変更できるメタテーブル (Metatable) を提供しており、各動作は対応するメタメソッドに関連付けられています。

たとえば、メタテーブルを使用して、Lua が 2 つのテーブルの加算演算 a+b を計算する方法を定義できます。

Lua が 2 つのテーブルを追加しようとすると、最初に 2 つのテーブルのいずれかにメタテーブルがあるかどうかを確認し、次に「__add」というフィールドがあるかどうかを確認します。見つかった場合は、対応する値が呼び出されます。 「__add」などのリアルタイム フィールドの場合、対応する値 (多くの場合、関数またはテーブル) は「メタメソッド」です。

メタテーブルを処理するための非常に重要な関数が 2 つあります:

  • setmetatable(table, metatable): 指定されたテーブルにメタテーブル (metatable) を設定します。メタテーブル (metatable) に __metatable キー値が存在する場合、setmetatable を実行します。失敗します。

  • getmetatable(table): オブジェクトのメタテーブルを返します。

次の例は、指定されたテーブルのメタテーブルを設定する方法を示しています:

mytable = {}                          -- 普通表 
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表

上記のコードは 1 行で直接記述することもできます:

mytable = setmetatable({},{})

以下は、返されるオブジェクト メタテーブルです:

getmetatable(mytable)                 -- 这回返回mymetatable

__index metaメソッド

これはメタテーブルであり、最も一般的に使用されるキーです。

キーによってテーブルにアクセスするとき、キーに値がない場合、Lua はテーブルのメタテーブル内で __index キーを検索します (メタテーブルがあると仮定して)。 __index にテーブルが含まれている場合、Lua はテーブル内で対応するキーを検索します。


lua コマンドを使用して対話モードに入り、表示することができます:

$ lua
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 } 
> t = setmetatable({}, { __index = other }) 
> t.foo
3
> t.bar
nil

__index に関数が含まれている場合、Lua はその関数を呼び出し、テーブルとキーがパラメーターとして関数に渡されます。

__index メタ メソッドは、要素がテーブルに存在するかどうかを確認し、存在しない場合は結果を nil に返します。

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
})

print(mytable.key1,mytable.key2)

インスタンスの出力結果は:

value1	metatablevalue

インスタンス分析:

  • mytableテーブルの割り当ては{key1 = "value1"}です。

  • mytable はメタテーブルを設定し、メタメソッドは __index です。

  • mytable テーブルで key1 を探します。見つかった場合は要素を返します。見つからない場合は続行します。

  • mytable テーブルで key2 を探します。見つかった場合は、metatablevalue を返します。見つからない場合は、続行します。

  • メタテーブルに __index メソッドがあるかどうかを確認します。__index メソッドが関数である場合は、その関数を呼び出します。

  • メタメソッドで、「key2」キーパラメータが渡されているかどうかを確認します(mytable.key2が設定されています)。「key2」パラメータが渡された場合は「metatablevalue」が返され、それ以外の場合は対応するキー値が返されます。 mytable に返されます。


上記のコードは次のように簡単に書くことができます:

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

概要

Lua でテーブル要素を見つけるためのルールは、実際には次の 3 つのステップです:

  • 1.見つかった場合は要素を返し、見つからない場合は続行します

  • 2. テーブルにメタテーブルがあるかどうかを判断します。メタテーブルがない場合は、nil を返します。

  • 3. メタテーブルに __index メソッドがあるかどうかを判断します。__index メソッドが nil の場合は nil を返し、__index メソッドが関数の場合は 1、2、3 を繰り返します。この関数の戻り値。


__newindex メタメソッド

__newindex メタメソッドはテーブルの更新に使用され、__index はテーブルにアクセスするために使用されます。

テーブルの欠落しているインデックスに値を割り当てると、インタープリターは __newindex メタメソッドを探します。存在する場合は、割り当てを実行せずにこの関数を呼び出します。

次の例は、__newindex メタメソッドの適用を示しています:

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)

上記の例の実行出力は次のとおりです:

value1
nil	新值2
新值1	nil

上記の例では、テーブルは、値を代入するときにメタメソッド __newindex を設定します。新しいインデックス キー (newkey) (mytable.newkey = "新しい値 2") の場合、値を割り当てずにメタ メソッドが呼び出されます。既存のインデックス キー (key1) が割り当てられている場合、メタ メソッド __newindex を呼び出さずに値が割り当てられます。

次の例では、rawset 関数を使用してテーブルを更新します:

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(mytable, key, value)
		rawset(mytable, key, "\""..value.."\"")

  end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

上記の例の実行出力は次のとおりです:

new value	"4"

演算子をテーブルに追加します

次の例は、2 つのテーブルの加算操作を示しています:

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, table_maxn(mytable)+1,newtable[i])
    end
    return mytable
  end
})

secondtable = {4,5,6}

mytable = mytable + secondtable
	for k,v in ipairs(mytable) do
print(k,v)
end

上記の実行例の出力 結果は次のとおりです:

1	1
2	2
3	3
4	4
5	5
6	6

__add キーはメタテーブルに含まれており、追加操作が実行されます。 表内の対応する演算リストは次のとおりです:

演算子 '-'. は、演算子 '..'. 対応する演算子 '=='. '<' に対応します。 は、演算子「<=」に対応します。__callメタメソッド
モード説明
__add対応する演算子 '+'.
__sub対応する演算子 '-'.
__mul は演算子 '*' に対応します。
__div は演算子 '/' に対応します。
__mod は演算子 '%' に対応します。うーん
__concat
__eq
__lt
__le
__callメタメソッドは、Luaが値を呼び出すときに呼び出されます。次の例は、テーブル内の要素の合計の計算を示しています。
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 定义元方法__call
mytable = setmetatable({10}, {
  __call = function(mytable, newtable)
	sum = 0
	for i = 1, table_maxn(mytable) do
		sum = sum + mytable[i]
	end
    for i = 1, table_maxn(newtable) do
		sum = sum + newtable[i]
	end
	return sum
  end
})
newtable = {10,20,30}
print(mytable(newtable))

上記の例の出力結果は次のとおりです。

70

__tostring Metamethod

__tostring メタメソッドは、テーブルの出力動作を変更するために使用されます。次の例では、テーブルの出力内容をカスタマイズしました:
mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
		sum = sum + v
	end
    return "表所有元素的和为 " .. sum
  end
})
print(mytable)

上記の例の実行の出力結果は次のとおりです:

表所有元素的和为 60

この記事から、メタテーブルはコード関数を非常に単純化できることがわかります。 Lua のメタテーブルを理解すると、よりシンプルで優れた Lua コードを書いてみましょう。