ディープラーニングLua-メタテーブル

メタメソッド

関数 説明
__追加 演算子+
__サブ オペレーター-
__私は持っています 演算子*
__ div オペレーター/
__モッド オペレーター%
__unm 演算子-(否定)
__concat オペレーター:
__eq 演算子==
__lt 演算子<
__the 演算子<=
__コール 関数が呼び出されたとき
__tostring 文字列に変換
__index インデックスを呼び出す
__newindex インデックスを割り当てる

これらの演算子は使用法が似ているため、個別に説明することはしません。次に、__ call、__ tostring、__ index、および__newindexの4つのメタメソッドについて説明します。

__コール

__callを使用すると、テーブルを関数として使用できます。

local mt = {}
--__call的第一参数是表自己
mt.__call = function(mytable,...)
    --输出所有参数
    for _,v in ipairs{...} do
        print(v)
    end
end

t = {}
setmetatable(t,mt)
--将t当作一个函数调用
t(1,2,3)

結果:

1
2
3

__tostring

__tostringは、テーブルを文字列に変換する動作を変更できます

local mt = {}
--参数是表自己
mt.__tostring = function(t)
    local s = "{"
    for i,v in ipairs(t) do
        if i > 1 then
            s = s..", "
        end
        s = s..v
    end
    s = s .."}"
    return s
end

t = {1,2,3}
--直接输出t
print(t)
--将t的元表设为mt
setmetatable(t,mt)
--输出t
print(t)

結果:

table: 0x14e2050
{1, 2, 3}

__index

テーブルの存在しないインデックスを呼び出す場合、メタテーブルの__indexメタメソッドが使用されます。以前のメタメソッドとは異なり、__ indexは関数またはテーブルにすることができます。
関数として:
テーブルとインデックスをパラメータとして__indexメタメソッドに渡し、戻り値を返します

local mt = {}
--第一个参数是表自己,第二个参数是调用的索引
mt.__index = function(t,key)
    return "it is "..key
end

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出
print(t.key)

結果:

nil
it is key

テーブルとして:
__ indexメタメソッドテーブルを見つけます。インデックスがある場合は、インデックスに対応する値を返します。それ以外の場合は、nilを返します。

local mt = {}
mt.__index = {key = "it is key"}

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key"
print(t.key)
--输出表中未定义,但元表的__index中也未定义的值时,输出为nil
print(t.key2)

結果:

nil
it is key
nil

__newindex

テーブル内の存在しないインデックスに値を割り当てると、メタテーブル内の__newindexメタメソッドが関数
として呼び出さ
ます.__ newindexが関数の場合、割り当てステートメント内のテーブル、インデックス、および割り当て値は次のようになりますパラメータと呼ばれます。テーブルに変更はありません

local mt = {}
--第一个参数时表自己,第二个参数是索引,第三个参数是赋的值
mt.__newindex = function(t,index,value)
    print("index is "..index)
    print("value is "..value)
end

t = {key = "it is key"}
setmetatable(t,mt)
--输出表中已有索引key的值
print(t.key)
--为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息
t.newKey = 10
--表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动
print(t.newKey)

結果:

it is key
index is newKey
value is 10
nil

テーブル
__newindexはテーブルであるため、tに存在しないインデックスに値を割り当てると、元のテーブルを変更せずに、__ newindexが指すテーブルにインデックスと値が割り当てられます。

local mt = {}
--将__newindex元方法设置为一个空表newTable
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)
--对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey"
t.newKey = "it is newKey"
print(t.newKey,newTable.newKey)

結果:

nil nil
nil it is newKey

rawgetとrawset

テーブルの値を直接変更または取得する場合は、rawgetメソッドとrawsetメソッドが必要になることがあります。
Rawgetを使用すると、メタテーブルの__indexメタメソッドを経由せずに、テーブル内のインデックスの実際の値を直接取得できます。

local mt = {}
mt.__index = {key = "it is key"}
t = {}
setmetatable(t,mt)
print(t.key)
--通过rawget直接获取t中的key索引
print(rawget(t,"key"))

結果:

it is key
nil

rawsetを使用すると、メタテーブルの__newindexメタメソッドを渡すことなく、テーブル内のインデックスに値を直接割り当てることができます。

local mt = {}
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)
--通过rawset直接向t的newKey索引赋值
rawset(t,"newKey","it is newKey")
print(t.newKey,newTable.newKey)

結果:

nil nil
it is newKey    nil

メタテーブルのシナリオを使用する

テーブルとしてメタテーブル

テーブルにメタテーブルを設定することで、オブジェクト指向プログラミングをLuaに実装できます。

ユーザーデータとしてのメタテーブル

Luaのcの構造体へのオブジェクト指向アクセスは、ユーザーデータとメタテーブルを介して実現できます。

 

弱い参照テーブル

 

Pythonなどのスクリプト言語と同様に、Luaも自動メモリ管理(ガベージコレクション)を使用します。プログラムは、オブジェクトを削除せずにオブジェクトを作成するだけで済みます。ガベージコレクションメカニズムを使用することで、Luaは期限切れのオブジェクトを自動的に削除します。ガベージコレクションメカニズムにより、プログラマーは、C言語で頻繁に発生するメモリリークや無効なポインター参照などの低レベルのバグから解放されます。

Pythonのガベージコレクションメカニズムは参照カウントアルゴリズムを使用していることがわかっています。オブジェクトを指すすべての名前が無効な場合(有効期間を超えるか、プログラマーが明示的にdelを超える場合)、オブジェクトが占有していたメモリが再利用されます。ただし、循環参照の場合は特殊なケースであり、ガベージコレクターは通常それを認識できません。これにより、循環参照を持つオブジェクトの参照カウンターがゼロになることはなく、リサイクルされる可能性はありません。

Pythonで循環参照を使用する例:

class main1:
    def __init__(self):
        print('The main1 constructor is calling...')
    def __del__(self):
        print('The main1 destructor is calling....')

class main2:
    def __init__(self, m3, m1):
        self.m1 = m1
        self.m3 = m3
        print('The main2 constructor is calling...')
    def __del__(self):
        print('The main2 destructor is calling....')

class main3:
    def __init__(self):
        self.m1  = main1()
        self.m2 = main2(self, self.m1)
        print('The main3 constructor is calling...')
    def __del__(self):
        print('The main3 destructor is calling....')

# test
main3()
       

出力は次のとおりです。

The main1 constructor is calling...
The main2 constructor is calling...
The main3 constructor is calling...

デストラクタ(__del__関数)が呼び出されておらず、循環参照によってメモリリークが発生していることがわかります。

 

ガベージコレクターは、ユーザーがガベージと見なすものではなく、ガベージと見なされるもののみを収集できます。たとえば、グローバル変数に格納されているオブジェクトは、プログラムがそれらを使用しなくなった場合でも、ユーザーがこれらのオブジェクトを解放できるようにnilに割り当てない限り、Luaにとってゴミではありません。ただし、参照をクリアするだけでは不十分な場合もあります。たとえば、オブジェクトが配列に配置されている場合、オブジェクトをリサイクルすることはできません。これは、現在他の場所で使用されていなくても、ユーザーがいない限り、配列が参照しているためです。 Luaへのこの参照は、このオブジェクトのリサイクルを妨げるべきではないことを伝えます。そうしないと、Luaはそれを知りません。

テーブルにはキーと値があり、どちらにも任意のタイプのオブジェクトを含めることができます。通常、ガベージコレクターは、アクセス可能なテーブルのキーまたは値であるオブジェクトを再利用しません。言い換えれば、これらのキーと値は強力な参照であり、参照されたオブジェクトのリサイクルを防ぎます。弱参照テーブルでは、キーと値をリサイクルできます。

弱い参照テーブル(弱いテーブル)は、参照がオブジェクトの回復を妨げてはならないことをLuaに伝えるためにユーザーによって使用されます。いわゆる弱参照は、ガベージコレクターによって無視されるオブジェクト参照です。オブジェクトの参照が弱参照である場合、オブジェクトもリサイクルされ、弱参照自体を何らかの形で削除できます。

弱参照テーブルには次の3つのタイプがあります。

1.弱い参照キーを
持つテーブル; 2。弱い参照値を
持つテーブル; 3。弱い参照キーと値が同時にあるテーブル;

テーブルの弱参照タイプは、メタテーブルの__modeフィールドによって決定されます。このフィールドの値は文字列である必要があります。
「k」が含まれている場合、このテーブルのキーは弱く参照されます。
「v」が含まれている場合、このテーブルの値は弱く参照されます。

 

テーブルを弱く参照する例を次に示します。これは、collectgarbage関数を使用してガベージコレクションを強制する方法です。

a = {1,4, name='cq'}

setmetatable(a, {__mode='k'})

key = {}
a[key] = 'key1'

key = {}
a[key] = 'key2'

print("before GC")
for k, v in pairs(a) do
    print(k, '\t', v)
end

collectgarbage()

print("\nafter GC")
for k, v in pairs(a) do
    print(k, '\t', v)
end

出力:

before GC
1                       1
2                       4
table: 0x167ba70                        key1
name                    cq
table: 0x167bac0                        key2

after GC
1                       1
2                       4
name                    cq
table: 0x167bac0                        key2

この例では、2番目の文の割り当てkey = {}が最初のキーを上書きします。コレクターが実行されると、最初のキーを参照する場所がないため、最初のキーがリサイクルされ、テーブル対応するエントリも削除されます。 。2番目のキーについては、可変キーが引き続き参照しているため、リサイクルされていません。

弱参照テーブル内のオブジェクトのみをリサイクルでき、数値、文字列、ブール値などの「値」はリサイクルできないことに注意してください。

 

 

メモ化関数は、時間にスペースを使用する方法です。たとえば、通常のサーバーが要求を受信した場合、コード文字列でloadstringを呼び出してから、コンパイルされた関数を呼び出す必要があります。ただし、loadstringは高価な関数です。サーバーに送信されるコマンドの中には、「close()」などの頻度が高いものがあります。このようなコマンドを受信するたびにloadstringが呼び出される場合は、サーバーに補助を使用させることをお勧めします。テーブルには、loadstringへのすべての呼び出しの結果が記録されます。

メモ機能の例:

local results = {}

setmetatable(results, {__mode='v'})

function mem_loadstring(s)

    local res = results[s]

    if res == nil then
        res=assert(loadstring(s))
        results[s]=res
    end

    return res
end 

local a = mem_loadstring("print 'hello'")
local b = mem_loadstring("print 'world'")

a = nil

collectgarbage()

for k,v in pairs(results) do
    print(k, '\t', v)
end

この例では、テーブルの結果は、サーバーが受信したすべてのコマンドとそのコンパイル結果を徐々に蓄積します。一定期間が経過すると、大量のメモリが消費されます。弱い参照テーブルでこの問題を解決できます。結果テーブルの参照値が弱い場合、すべてのガベージコレクションは、実行中に未使用のコンパイル結果をすべて削除します。

 

記事Luaのメタテーブル、どのようにデフォルト値を使用してテーブルを実装する言及されました。各テーブルにデフォルト値を設定し、これらのデフォルト値を保持したくない場合は、次の例のように、テーブルへの弱参照を使用することもできます。

local defaults = {}

setmetatable(defaults, {__mode='k'})

local mt = {__index=function(t) return defaults[t] end}

function setDefault(t, d)
    defaults[t] = d
    setmetatable(t, mt)
end 


local a = {}
local b = {}

setDefault(a, "hello")
setDefault(b, "world")

print(a.key1)
print(b.key2) 

b = nil
collectgarbage()

for k,v in pairs(defaults) do
    print(k,'\t',v)
end

弱参照の適用:Luaでのメモリリーク検出

Luaのメモリリークを検出するための2つのレベルもあります。

レベル1:luaレイヤー内のluaオブジェクトのリークと、luaレイヤーによって引き起こされたcオブジェクトのリークを同時に検出します。

レベル2:luaレイヤー内のluaオブジェクトのリークのみを検出します。

レベル1を満たすメモリリークの検出は比較的少し複雑ですが、原則はまだ比較的単純です。つまり、2つの時点でlua状態をスキャンしてスナップショットを取得し、2つのスナップショットを比較します。後者は次のカードの時に割り当てられたメモリが前のカードのメモリよりも多いです。これらの2つの時点が機能的に論理的に一貫している場合(たとえば、前の時点がコピーに入る前であり、次の時点がコピーを出た後である場合)、追加のメモリ参照はリークされたメモリです。

ここでは、レベル2の実装に焦点を当てます。luaレイヤー内のluaオブジェクトのリークのみを検出することも非常に重要です。コードスケールが大きく、鋭い視力と厳密な脳分析によってメモリリークを検出することは非常に難しいため、自分自身を助けるための特定のツールが必要です。ここでは、luaが提供する「弱参照」を使用できます。「弱い参照」は一種の参照です。オブジェクトへの参照は、lua gcメカニズムによるオブジェクトのガベージコレクションに影響を与えません。つまり、オブジェクトに弱い参照しかない場合、オブジェクトはgcによって再利用されます。 -要するに、gcは弱い参照を無視します。

したがって、キー値とコンテンツ値への参照が「弱参照」(つまり、メタテーブルの__mode要素属性値が「kv」)であるグローバルメモリリーク監視弱参照テーブルを確立できます。気になるオブジェクトテーブルに配置します。次に、このオブジェクトがリサイクルされるべきだと思われる瞬間を待ち、テーブルにこのオブジェクトがまだあるかどうかを確認します。存在しない場合は、オブジェクトが正しくリサイクルされたことを意味します。存在する場合は、オブジェクトが正しくリサイクルされていないことを意味します。つまり、対象が漏れました。

おすすめ

転載: blog.csdn.net/Momo_Da/article/details/98732365