الطرق الوصفية الحسابية
أعد تعريف + و- و* و/ والمعاملات الأخرى باستخدام الطرق الوصفية
الطرق الوصفية الحسابية درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.
أسماء دوال الميتا
تعرّف Lua دوال ميتا لجميع عوامل الحساب. عندما ترى Lua عملية حسابية على جدول، فإنها تبحث عن دالة الميتا المقابلة في جدول ميتا الجدول. وإذا كان كلا المعاملين جدولين، تتحقق Lua أولًا من جدول ميتا المعامل الأيسر.
-- Operator -> Metamethod
-- a + b -> __add
-- a - b -> __sub
-- a * b -> __mul
-- a / b -> __div
-- a % b -> __mod
-- a ^ b -> __pow
-- a // b -> __idiv
-- -a -> __unm (unary minus)
-- #a -> __len
print("Metamethods defined for all Lua operators")__add لجمع المتجهات
عرّف __add في جدول المتجه لدعم العامل +. تستقبل دالة الميتا معاملين وتعيد قيمة جديدة. وقد يكون كلا المعاملين جدولين أو قد يكون أحدهما عددًا، لذا ينبغي أن تتعامل دالة الميتا مع كلتا الحالتين.
local Vec = {}
Vec.__index = Vec
Vec.__add = function(a, b)
return setmetatable({x=a.x+b.x, y=a.y+b.y}, Vec)
end
Vec.__tostring = function(v)
return string.format("Vec(%g,%g)", v.x, v.y)
end
function Vec.new(x,y) return setmetatable({x=x,y=y},Vec) end
local v1 = Vec.new(1,2)
local v2 = Vec.new(3,4)
print(tostring(v1 + v2)) -- Vec(4,6)__sub و__mul و__div
عرّف __sub و__mul و__div بالطريقة نفسها. وبالنسبة إلى الضرب، تعامل مع حالتي table*table وtable*number من خلال التحقق من نوعي المعاملين داخل دالة الميتا.
local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__sub = function(a,b) return V(a.x-b.x, a.y-b.y) end
Vec.__mul = function(a,b)
if type(a)=="number" then return V(a*b.x, a*b.y)
elseif type(b)=="number" then return V(a.x*b, a.y*b)
else return a.x*b.x + a.y*b.y -- dot product
end
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end
local v = V(3,4)
print(tostring(v * 2)) -- (6,8)
print(tostring(V(1,0) - V(0,1))) -- (1,-1)__unm للنفي الأحادي
__unm هي دالة ميتا للنفي الأحادي، وتُستدعى عندما تقيّم Lua التعبير -obj. وهي تستقبل المعامل، وتُمرَّر إليها القيمة نفسها مرتين للاتساق، ثم تعيد القيمة المنفية.
local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__unm = function(a) return V(-a.x, -a.y) end
Vec.__tostring = function(v)
return string.format("(%g,%g)", v.x, v.y)
end
local v = V(3, -4)
print(tostring(-v)) -- (-3,4)
print(tostring(-(-v))) -- (3,-4)__len للطول المخصص
يُستدعى __len عند تقييم #obj على جدول لا يحتوي على تسلسل صحيح خام. عرّفه ليعيد «طولًا» ذا معنى للأنواع المخصصة مثل المجموعات أو الطوابير أو الحاويات الموزونة.
local Bag = {}
Bag.__index = Bag
Bag.__len = function(b) return b._count end
function Bag.new()
return setmetatable({_count=0, _items={}}, Bag)
end
function Bag:add(item, qty)
self._items[item] = (self._items[item] or 0) + (qty or 1)
self._count = self._count + (qty or 1)
end
local bag = Bag.new()
bag:add("apple", 3)
bag:add("banana", 2)
print(#bag) -- 5__mod و__pow
يتعامل __mod مع العامل %، بينما يتعامل __pow مع العامل ^. وهما أقل شيوعًا، لكنهما مفيدان للأنواع العددية المخصصة مثل الأعداد الصحيحة الكبيرة والكسور أو كائنات الحساب المعياري.
local Modular = {}
Modular.__index = Modular
local M = 1000000007
local function mod(n)
return setmetatable({n = n % M}, Modular)
end
Modular.__add = function(a,b) return mod(a.n + b.n) end
Modular.__mul = function(a,b) return mod(a.n * b.n) end
Modular.__pow = function(a,b)
local r, base, exp = mod(1), a, b
while exp > 0 do
if exp%2==1 then r = r*base end
base = base*base; exp = math.floor(exp/2)
end
return r
end
print((mod(2) ^ 30).n) -- 73741817 (2^30 mod 1e9+7)__concat
يُفعَّل __concat بواسطة العامل .. عندما يملك أحد المعاملين على الأقل دالة الميتا هذه. ويتيح لك ذلك تعريف التسلسل للأنواع المخصصة أو إنشاء لغات خاصة بالمجال ذات أسلوب متسلسل.
local Builder = {}
Builder.__index = Builder
Builder.__concat = function(a, b)
local result = {}
for _, v in ipairs(a._parts) do result[#result+1] = v end
if type(b) == "string" then result[#result+1] = b
else for _, v in ipairs(b._parts) do result[#result+1] = v end end
return setmetatable({_parts=result}, Builder)
end
Builder.build = function(b) return table.concat(b._parts) end
local function S(s) return setmetatable({_parts={s}},Builder) end
print((S("Hello") .. ", " .. S("World") .. "!"):build())دوال ميتا للمقارنة
يعرّف __eq العامل == للجداول، ولا يُستدعى إلا عندما يملك المعاملان جدول الميتا نفسه. ويعرّف __lt العامل <، بينما يعرّف __le العامل <=. ملاحظة: يستخدم ~= الدالة __eq؛ ويستخدم > الدالة __lt مع عكس المعاملين؛ ويستخدم >= الدالة __le.
local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__eq = function(a,b) return a.x==b.x and a.y==b.y end
Vec.__lt = function(a,b)
return (a.x^2+a.y^2) < (b.x^2+b.y^2) -- by magnitude
end
print(V(1,2) == V(1,2)) -- true
print(V(1,2) == V(1,3)) -- false
print(V(1,0) < V(2,0)) -- true (1 < 2)تسلسل دوال الميتا
بما أن دوال الميتا تعيد قيمًا، عادةً جداول جديدة، يمكن تسلسل العمليات بصورة طبيعية. يعيد كل عامل كائنًا جديدًا، ويستخدم العامل التالي تلك النتيجة. ويتيح ذلك كتابة تعبيرات حسابية متسلسلة على الأنواع المخصصة.
local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__add = function(a,b) return V(a.x+b.x,a.y+b.y) end
Vec.__mul = function(a,b)
if type(b)=="number" then return V(a.x*b,a.y*b) end
return V(a.x*b.x+a.y*b.y, 0) -- simplified
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end
local result = V(1,0) + V(0,1) * 3
print(tostring(result)) -- (1,3)العمليات على الأنواع المختلطة
عند كتابة vec * 2، يكون المعامل الأيسر هو الجدول. وعند كتابة 2 * vec، يكون المعامل الأيسر عددًا، ولا يملك دالة ميتا، لذلك تتحقق Lua من جدول ميتا المعامل الأيمن بحثًا عن __mul. تعامل مع الترتيبين كليهما في دالة الميتا.
local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__tostring = function(v) return "("..v.x..","..v.y..")" end
Vec.__mul = function(a, b)
if type(a) == "number" then
return V(a*b.x, a*b.y) -- scalar * vec
elseif type(b) == "number" then
return V(a.x*b, a.y*b) -- vec * scalar
end
return a.x*b.x + a.y*b.y -- dot product
end
print(tostring(V(1,2) * 3)) -- (3,6)
print(tostring(3 * V(1,2))) -- (3,6)تحقق سريع
ما دالة الميتا التي يفعّلها التعبير -myTable؟
مراجعة: دوال الميتا الحسابية
الملخص:
__add __sub __mul __div __mod __pow __idiv— الحساب الثنائي__unm— النفي الأحادي؛__len— العامل #__concat— العامل..__eq __lt __le— عوامل المقارنة- تعامل مع الأنواع المختلطة، العدد/الجدول، داخل دوال الميتا
الأسئلة الشائعة
هل درس «الطرق الوصفية الحسابية» مجاني؟
نعم — نص درس «الطرق الوصفية الحسابية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.
ماذا ستتعلم في «الطرق الوصفية الحسابية»؟
أعد تعريف + و- و* و/ والمعاملات الأخرى باستخدام الطرق الوصفية تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟
لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «الطرق الوصفية الحسابية»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟
نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مقدمة إلى جداول البيانات الوصفية
- __index و__newindex
- الطرق الوصفية الحسابية
- __tostring و__len