English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。
因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定table设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable).
以下示例演示了如何对指定的表设置元表:
mytable = {}......--普通表 mymetatable = {}......--元表 setmetatable(mytable,mymetatable)......--把mymetatable设为mytable的元表
يمكن كتابة الكود أعلاه كخط واحد أيضًا:
mytable = setmetatable({},{})
هذا هو جدول المعلومات للعنصر العودة:
getmetatable(mytable) -- سيتم العودة إلى mymetatable
هذا هو المفتاح الأكثر استخدامًا في جدول المعلومات.
عندما تتم زيارة table عبر مفتاح، إذا لم يكن لديه قيمة، فإن Lua سيبحث عن مفتاح __index في جدول المعلومات للtable (تقديرًا بوجود جدول المعلومات)
يمكننا التحقق من ذلك باستخدام أوامر lua للدخول إلى الوضع التفاعلي:
$ lua Lua 5.3.0 حقوق النشر (C) 1994-2015 Lua.org, PUC-Rio > other = { foo = 3 } > t = setmetatable({}, { __index = other }) > t.foo 3 > t.bar nil
إذا كان يحتوي __index على دالة، فإن Lua سيقوم بتشغيل تلك الدالة، ويتم تمرير جدول و المفتاح كمعاملين إلى الدالة.
__index الطريقة الأساسية تفحص ما إذا كان العنصر موجودًا في الجدول، إذا لم يكن موجودًا، يتم العودة إلى nil؛ وإذا كان موجودًا، يتم العودة إلى النتيجة التي تقدمها __index.
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.
ابحث عن key1 في جدول mytable، إذا تم العثور عليه، يتم العودة إلى العنصر، وإذا لم يتم العثور عليه، يتم المتابعة.
ابحث عن key2 في جدول mytable، إذا تم العثور عليه، يتم العودة إلى 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;如果 __index 方法是一个函数,则返回该函数的返回值。
该部分内容来自作者寰子:https://blog.csdn.net/xocoder/article/details/9028347
__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"
以下示例演示了两表相加操作:
-- 计算表中最大值,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 键包含在元表中,并进行相加操作。 表中对应的操作列表如下:(注意:__是两个下划线)
模式 | 描述 |
---|---|
__add | 对应的运算符 '+'. |
__sub | 对应的运算符 '-'. |
__mul | 对应的运算符 '*'. |
__div | 对应的运算符 '/'. |
__mod | 对应的运算符 '%'. |
__unm | 对应的运算符 '-'. |
__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 -- تعريف الميثود meta __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 لتحديد سلوك إخراج الجدول. في المثال التالي، قمنا بتعريف محتوى إخراج الجدول بشكل مخصص:
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
من خلال هذا المقال يمكننا معرفة أن المetatableView يمكن أن يبسط وظائفنا بشكل جيد، لذا ففهم metatable في Lua يمكن أن يساعدنا في كتابة كود Lua بسيط وأفضل.