تتيح جداول الميتا للجداول أن تصبح أكثر قوة مما كانت عليه من قبل. يتم إرفاقها بالبيانات وتحتوي على قيم تسمى دوال الميتا. يتم تفعيل دوال الميتا عندما يتم استخدام عمل معين مع البيانات المرفقة بها.
التلاعب بجداول الميتا
وظيفتان رئيستان لإضافة جدول الميتا والعثور عليه هما setmetatable() و getmetatable().
local x = {}local metaTable = {} -- جداول الميتا هي أيضا جداول!setmetatable(x, metaTable) -- أعطِ x جدول ميتا يسمى metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
تقوم وظيفة setmetatable() أيضًا بإرجاع الجدول الذي تقوم بتعيين جدول الميتا له، لذا فإن هذه السكربتين تفعلان نفس الشيء:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
دوال الميتا
دوال الميتا هي الوظائف المخزنة داخل جدول الميتا. يمكن أن تقوم بالتحويل من استدعاء جدول، إلى جمع جدول، إلى حتى تقسيم الجداول كذلك. إليك قائمة بدوال الميتا المتاحة:
| الأسلوب | الوصف |
|---|---|
| __index(table, index) | يتم تفعيله عندما يتم فهرسة table[index]، إذا كانت table[index] هي nil. يمكن أيضًا تعيينه إلى جدول، في هذه الحالة سيتم فهرسة ذلك الجدول. |
| __newindex(table, index, value) | يتم تفعيله عندما تحاول table[index] أن تُعيّن (table[index] = value)، إذا كانت table[index] هي nil. يمكن أيضًا تعيينه إلى جدول، في هذه الحالة سيتم فهرسة ذلك الجدول. |
| __call(table, ...) | يتم تفعيله عند استدعاء الجدول كدالة، ... هي الوسائط التي تم تمريرها. |
| __concat(table, value) | يتم تفعيله عندما يتم استخدام عامل الدمج .. على الجدول. |
| __unm(table) | يتم تفعيله عند استخدام عامل العدد المفرد – على الجدول. |
| __add(table, value) | عامل الجمع +. |
| __sub(table, value) | عامل الطرح –. |
| __mul(table, value) | عامل الضرب *. |
| __div(table, value) | عامل القسمة /. |
| __idiv(table, value) | عامل القسمة الأرضية //. |
| __mod(table, value) | عامل المودول %. |
| __pow(table, value) | عامل الأس ^. |
| __tostring(table) | يتم تفعيله عند استدعاء tostring على الجدول. |
| __metatable | إذا كانت موجودة، تؤمن جدول الميتا بحيث أن getmetatable() ستعيد هذا بدلاً من جدول الميتا وأن setmetatable() ستسبب خطأ. قيمة غير دالة. |
| __eq(table, value) | عامل المساواة ==¹ |
| __lt(table, value) | عامل أقل من <¹ |
| __le(table, value) | عامل أقل أو يساوي <=¹ |
| __mode | يستخدم في الجداول الضعيفة، ليحدد ما إذا كانت المفاتيح و/أو القيم في الجدول ضعيفة. لاحظ أن المراجع إلى مثيلات Roblox ليست ضعيفة أبدًا. الجداول التي تحتفظ بمثل هذه المراجع لن يتم جمعها بواسطة جامع القمامة. |
| __len(table) | يتم تفعيله عند استخدام عامل الطول # على الكائن. |
| __iter(table) | يستخدم للدلالة على متكرر مخصص عند استخدام التكرار المعمم. |
يجب الإشارة إلى أنه عند كتابة دوال إما للأرقام أو دوال الميتا العلاقية، فإن بارامترين الدالة قابلان للتبادل بين الجدول الذي أطلق دالة الميتا والقيمة الأخرى. على سبيل المثال، عند القيام بعمليات المتجه مع الكميات، فإن القسمة ليست تبادلية. لذلك إذا كنت تكتب دوال ميتا لفئة vector2 الخاصة بك، يجب أن تكون حذرًا في حساب أي سيناريو.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a هو عدد، b هو متجه
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a هو متجه، b هو عدد
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- كل من a و b هما متجهات
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- -3.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5
الاستخدام
هناك العديد من الطرق لاستخدام جداول الميتا، على سبيل المثال، استخدام دالة الميتا __unm لجعل جدول سالبًا:
local metatable = {
__unm = function(t) -- __unm يتعلق بعامل العدد المفرد -
local negated = {}
for key, value in t do
negated[key] = -value -- عكس جميع القيم في هذا الجدول
end
return negated -- إرجاع الجدول
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
إليك طريقة مثيرة لتعيين الأشياء باستخدام __index:
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
تم تفعيل __index عندما تم فهرسة x في الجدول ولم يتم العثور عليه. ثم قامت Luau بالبحث في جدول __index عن فهرس يسمى x، وبتحقيق ذلك، أعادت القيمة.
الآن يمكنك بسهولة فعل ذلك باستخدام دالة بسيطة، ولكن هناك المزيد من حيث جاءت من. خذ هذه المثال:
local t = {10, 20, 30}print(t(5))
عادة لا يمكنك استدعاء جدول، لكن باستخدام جداول الميتا يمكنك:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- أضف الوسيطة (5) إلى القيمة، ثم ضعها في الجدول الجديد (t).
end
return unpack(sum) -- إرجاع قيم الجدول الفردية
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
يمكنك القيام بالمزيد أيضًا، مثل إضافة الجداول:
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
سيؤدي هذا إلى خطأ قائلاً أنك تحاول تنفيذ عمليات حسابية على جدول، لكن يمكن أن يعمل عند المحاولة مع جدول ميتا:
local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end
عند اللعب مع جداول الميتا، قد تواجه بعض المشكلات. إذا كنت بحاجة إلى استخدام دالة الميتا __index لإنشاء قيم جديدة في جدول، لكن جدول الميتا لذلك الجدول يحتوي أيضًا على دالة الميتا __newindex، فستحتاج إلى استخدام الدالة المضمنة في Luau rawset() لتعيين القيمة دون تفعيل أي دوال ميتا. خذ الشفرة التالية كمثال لما يحدث إذا لم تستخدم هذه الدوال.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- لا تعيّن القيم إلى الجدول بالطريقة العادية
end
})
print(t[1]) -- يسبب تدفق مكدس
تحدث تدفقات المكدس عندما تحاول استدعاء وظيفة من نفسها عدة مرات. في دالة __index أعلاه، يتم تعيين self[i] إلى قيمة، لذا عندما يصل إلى السطر التالي، يجب أن يكون self[i] موجودًا ومن المفترض ألا يستدعي دالة __index مرة أخرى. المشكلة هي أن __newindex لا تسمح لك بتعيين القيمة. إن وجودها يمنع إضافة القيم إلى الجدول بالطريقة القياسية t[i] = v. للتجاوز عبر ذلك، استخدم دالة rawset():
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- لا تعيّن القيم إلى الجدول بالطريقة العادية
end
})
print(t[1]) --> 10
استخدم نوع المجموعة
المجموعة هي مجموعة من العناصر بدون ترتيب وبدون عناصر مكررة. إما أن تكون أو لا تكون العنصر موجودًا داخل المجموعة. باستخدام جداول الميتا، يمكنك بناء والتلاعب بالمجموعات داخل سكربتات Luau.
الطرق الأساسية
تشمل الشفرة التالية الوظائف الأساسية للمجموعة، مما يتيح لك بناء مجموعات جديدة، إضافة وإزالة عنصر، التحقق مما إذا كانت المجموعة تحتوي على عنصر، وإخراج محتويات المجموعة.
local Set = {}
Set.__index = Set
-- دالة لبناء مجموعة من قائمة عناصر اختيارية
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- دالة لإضافة عنصر إلى مجموعة
function Set:add(item)
self[item] = true
end
-- دالة لإزالة عنصر من مجموعة
function Set:remove(item)
self[item] = nil
end
-- دالة للتحقق مما إذا كانت المجموعة تحتوي على عنصر
function Set:contains(item)
return self[item] == true
end
-- دالة لإخراج المجموعة كقائمة مفصولة بفواصل لأغراض التصحيح
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
إنشاء مجموعة
يمكن بناء مجموعة جديدة عن طريق استدعاء Set.new() مع مصفوفة اختيارية من العناصر لإضافتها.
local fruits = Set.new({"تفاح", "ليمون", "برتقال", "كرز", "لايم", "خوخ"})
لاحظ أنه حسب التعريف، ليس للمجموعة مفهوم ترتيب.
إضافة عنصر
يمكن إضافة عنصر إلى مجموعة موجودة عبر دالة Set:add().
local fruits = Set.new({"تفاح", "ليمون", "برتقال", "كرز", "لايم", "خوخ"})fruits:add("مانجو")
إزالة عنصر
لإزالة عنصر من مجموعة، استدعِ Set:remove() مع اسم العنصر.
local fruits = Set.new({"تفاح", "ليمون", "برتقال", "كرز", "لايم", "خوخ"})fruits:remove("برتقال")
التحقق من وجود عنصر
للتحقق مما إذا كانت مجموعة تحتوي على عنصر معين، استخدم Set:contains().
local fruits = Set.new({"تفاح", "ليمون", "برتقال", "كرز", "لايم", "خوخ"})local result1 = fruits:contains("كرز")print(result1) -- truelocal result2 = fruits:contains("بطيخ")print(result2) -- false
طرق إضافية
يمكن تنفيذ عمليات أخرى مفيدة للمجموعات، مما يتيح لك مقارنة العناصر بين المجموعات، دمج المجموعات، أو طرح مجموعة من أخرى.
التقاطع
عند اعتبار المجموعات كرسوم بيانية فين، يمكنك الحصول على تقاطع مجموعتين كما يلي، مما يعني العناصر التي تظهر في كلا المجموعتين.
local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"مانجو", "ليمون", "برتقال", "كرز", "لايم", "خوخ"})
local frozenFruits = Set.new({"مانجو", "خوخ", "أناناس"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- مانجو, خوخ
الاتحاد
يمكنك الحصول على الاتحاد لمجموعتين باستخدام الوظيفة التالية، مما يعني مجموعة من العناصر في كلتا المجموعتين بدون تكرار. لاحظ أن هذه الوظيفة تستخدم دالة الميتا __add لتوفير اختصار جمع set1 + set2.
function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"تفاح", "مانجو", "كرز", "خوخ"})
local sourFruits = Set.new({"ليمون", "لايم"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- خوخ, لايم, تفاح, كرز, ليمون, مانجو
الطرح
يمكنك إزالة جميع العناصر في مجموعة من العناصر في مجموعة أخرى عبر الوظيفة التالية. مشابهة للوظيفة أعلاه، تستخدم هذه دالة الميتا __sub لتوفير اختصار الطرح set1 - set2.
function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"تفاح", "ليمون", "مانجو", "كرز", "لايم", "خوخ"})
local sourFruits = Set.new({"ليمون", "لايم"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- مانجو, تفاح, كرز, خوخ