LUA言語の不思議な_indexと_newindex

12807 ワード

今日はLUA言語の中で最も強力な機能と言えることを学びました.indexと_newindex.読み終わったばかりで忘れないうちに、急いでメモを書いて落ち着いてください.
1.レビューメタテーブル(metatable)
LUA言語では、各値に定義済みのアクションセットがあり、文字列接続、削除などの2つの数を減算できます.ただし、LUAではtable変数の操作は定義されていません.table変数の内容が複雑すぎて、数値や文字列もあれば、関数やもう一つのtableなどの奇抜な変数もあることは理解できます.この場合、2つのtable間でどのように操作すべきかを定義するには、メタテーブルというアシスタントが必要です.メタテーブルを設定するには、一般的に次の方法を使用します.
t1 = {}
setmetatable(t, t1)     --t   table, t1     metatable

本で述べたように、どのtableも任意の値のメタテーブルとすることができ、1組のtableは共通のメタテーブルを共有することができ、tableは自身のメタテーブルに設定することもできる.メタテーブルと元の方法の概念を利用して、私たちは自分で2つのtableの交差、並列などの操作を定義することができますが、これは今日の重点ではありませんので、展開しないで、興味があれば関連書籍とblogを見ることができます.
2. __index大法
まず、次の状況を見てみましょう.
local tab = { 
    name="haha" 
}
print(tab.date)

見てみればprintはtabには存在しないインデックスにアクセスしているので、結果は必ずnilであることがわかります.これもLUA言語の常識です.では、例外はあるのでしょうか.答えはあります.実際には、tableに存在しないインデックス変数にアクセスした場合、解釈器は別のステップで動作します.検索_indexメタメソッドは、一般的にこのメソッドを継承と呼ぶ.Indexは一般的にユーザーが自分で作成します.上記の例に基づいて、プログラムを少し修正します.
local tab = { 
    name = "haha" 
}
local mt = {
    __index = function(t, key)      --             
        print("no such an index!")
    end
}
setmetatable(tab, mt)
print(tab.date)

以上のコードの機能は、tabにアクセスしようとするとdateという存在しない変数は、_indexの存在は、そのprintを実行し、dateというインデックスがないことを教えます.もちろん、デフォルト値を変更するなど、興味深い操作もできます.
local mt = {
    __index = function(t, key)
        return "empty"
    end
}

このときprint(tab.date)文を実行した結果、「empty」となります.なぜなら、空のインデックスに対して、戻り値はすべて「empty」であることを規定しているからです.もう1つの呼び出し方法は、次のとおりです.
local tab_old = { 
    name = "haha",
    date = "2017.1.7"
}
local tab = { 
    name = "haha" 
}
local mt = {
    __index = tab_old
}
setmetatable(tab, mt)
print(tab.date)

プログラムがtabにアクセスしようとするとdateでは、このインデックスがないため、tabに関連するindexメタメソッドを探し、indexはtab_に関連するoldという表なので、プログラムはtab_にあります.oldでdateインデックスを探します.
以上は簡単な例ですが、脳の穴を開けて、他の面白い操作を書くこともできます.注意すべきは、_indexはメタメソッドなので、機能を実現するにはtableに対応するメタテーブルを設定する必要があります.
3. __newindex大法
__newindexはもう一つの面白いメタメソッドです.と_indexには若干の違いがあります:_indexが存在しないインデックスにアクセスするとトリガーされますが、_newindexの存在しないインデックスに対する付与動作がトリガーされます.どう見てもいい友达だ.まず簡単な例を見てみましょう.
local tab = { 
    name="haha" 
}
tab.date = "2017.1.7"
print(tab.date)

上記の文を実行する限り、出力される値は自然に2017.1.7である.しかし、上記の付与動作は端末では示されず、tabに対する新しい付与操作を監視するには_newindexが出馬しました.
--  ,    !
local tab = { 
    name="haha" 
}
local mt = {
    __newindex = function(t, k, v)
        print("Successfully set element \"" .. tostring(k) ..
            "\" as " .. tostring(v))
    end
}
setmetatable(tab, mt)
tab["date"] = "2017.1.7"
print(tab.date)

覚えておいてください.newindexは、存在しないインデックスを付与する場合にのみトリガーされます.前の例でdateをnameに変更した場合、「Successfully set...」はありません.文出力.自分でコードを実行してみて、最後にprintが出力した文が間違っていることに気づいたのではないでしょうか.はい、そうです.newindexでは、出力文を規定しているだけで、本当の付与操作はしていません.だから、通常の考え方に従って、このように変えましょう.
--  ,    !
local mt = {
    __newindex = function(t, k, v)
        print("Successfully set element \"" .. tostring(k) ..
            "\" as " .. tostring(v))
        t[k] = v
    end
}

あまり早く喜ばないで、実は.のそんなに簡単ではありません.ブロガーはt[k]=vやreturn t[k]やreturn vなどの方法を試みたが,例外なく失敗した.具体的な原因はちょっと複雑かもしれませんが、先に続けてみてください.私たちはしばらく上の理由が間違っているのか分かりませんが、私たちはどうすればいいか知っています.Indexとnewindexが空のインデックスにトリガされている以上、エージェントの考えを利用して問題を解決することができます.直接本に貼った例:
t = {}
local _t = t    --    
t = {}      --         ,  _t  t       ,   

local mt = {
    __index = function (t, k)
        print("*access to element " .. tostring(k))
        return _t[k]
    end,
    __newindex = function(t, k, v)
        print("Successfully set element \"" .. tostring(k) ..
            "\" as " .. tostring(v))
         _t[k] = v
    end
}
setmetatable(t, mt)
t[2] = "hello"
print(t[2])

出力は次のとおりです.
Successfully set element "2" as hello
*access to element 2
hello

これで、上の問題はやっと解決しました.ただし、indexとnewindexの最後の戻り値は_t[k]操作の、すべての付与は最後にすべて与えられました_t[k]は、tへのアクセスが実際にはすべて正しい_t[k]のアクセス.これも「代理」の意味です.どんなに振り回されても、最後には_t,tは永遠に空tableであり,はっきり言ってtは傀儡である(細思極恐).では、上記の例のこの間違いを説明することができますか.
--  ,    !
local mt = {
    __newindex = function(t, k, v)
        print("Successfully set element \"" .. tostring(k) ..
            "\" as " .. tostring(v))
        t[k] = v
    end
}

私はここで展開しないで、みんなは自分で考えてみてください(ヒント:__newindexの内部でも__newindexはトリガーされます).
4. __indexと_newindexコンビネーション
__をindexと_newindexの2つの神器を組み合わせると、多くの面白いものを作ることができ、上の追跡アクセスは一例と言える.
読み取り専用テーブル
前例のnewindex文を少し変更すれば、読み取り専用tableを作成できます.
t = {
    name = "Jiang",
    date = "1926.8"
}
local _t = t
t = {}
local mt = {
    __index = function (t, k)
        print("*access to element " .. tostring(k))
        return _t[k]
    end,
    __newindex = function(t, k, v)
        print("table t is read-only!!")
    end
}
setmetatable(t, mt)
t["date"] = "1926.9"
t["sex"] = "male"
print(t["date"])
print(t["sex"])

tを変更しようとするすべての操作は禁止されています.tは空の変数であるため、tの書き込み操作は必ずトリガーされます.newindex.
1つのtableで別のtableに値を割り当てる
この例は別のブログのコードを直接転載します(http://www.jb51.net/article/55155.htm)(私のパソコン端末は中国語がサポートされていないため、全英語に変更されました)
local smartMan = {
    name = "none"
}
local other = {
    name = "hello, I'm innocent table"
}
local t1 = {}
local mt = {
    __index = smartMan,
    __newindex = other
}
setmetatable(t1, mt)
print("other's name(before): " .. other.name)
t1.name = "thief"
print("other's name(after): " .. other.name)
print("t1's name: " .. t1.name)

出力の結果:
other's name(before): hello, I'm innocent table
other's name(after): thief
t1's name: none

我々はt 1に値を付与しようとしたが,t 1は本来空であるため,_に基づいてnewindexの定義はotherテーブルに移り、実際に割り当てられたのはotherであり、t 1は依然として空の変数である.最後の文はt 1にアクセスしようとする.name,根拠_indexはsmartManという変数に移動し、印刷された値は実際にsmartManです.
同時にいくつかのtableを監視する
このコードは本から抜粋して、まだ自分で検証していないので、時間があればゆっくり補充しましょう.とにかくこの穴も埋め終わった.の
local index = {}
local mt = {
    __index = function(t, k)
        print("*access to element " .. tostring(k))
        return t[index][k]
    end,
    __newindex = function(t, k, v)
        print("*update of element " .. tostring(k) ..
            " to " .. tostring(v))
        t[index][k] = v
    end
}
function track(t)
    local proxy = {}
    proxy[index] = t
    setmetatable(proxy, mt)
    return proxy
end

t = track(t)