0Pricing
Lua Academy · درس

‏__tostring و__len

تحكّم في التمثيل النصي ومعامل # عبر الطرق الوصفية

‏__tostring و__len درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.

دالة الميتا __tostring

تُستدعى __tostring عند استدعاء tostring(obj)، وكذلك بواسطة print()، التي تستدعي tostring لكل معامل. عرّفها لتوفير تمثيل مقروء للبشر للكائنات المخصصة.

local Point = {}
Point.__index = Point
Point.__tostring = function(p)
  return string.format("Point(%.2f, %.2f)", p.x, p.y)
end

function Point.new(x,y)
  return setmetatable({x=x,y=y}, Point)
end

local p = Point.new(3.14, 2.72)
print(p)            -- Point(3.14, 2.72)
print(tostring(p))  -- Point(3.14, 2.72)

__len للتسلسلات

يُستدعى __len عند تطبيق العامل # على جدول يملك دالة الميتا هذه. في Lua 5.2 والإصدارات الأحدث، يُستدعى __len دائمًا للجداول؛ أما في الإصدار 5.1 فلا يُستدعى إلا لـ userdata. استخدمه لتعريف «الطول» للحاويات المخصصة.

local Queue = {}
Queue.__index = Queue
Queue.__len = function(q) return q._tail - q._head + 1 end

function Queue.new()
  return setmetatable({_data={},_head=1,_tail=0}, Queue)
end
function Queue:push(v)
  self._tail = self._tail + 1
  self._data[self._tail] = v
end
function Queue:pop()
  if self._head > self._tail then return nil end
  local v = self._data[self._head]
  self._head = self._head + 1
  return v
end

local q = Queue.new()
q:push("a"); q:push("b"); q:push("c")
print(#q)   -- 3

__tostring غني لتصحيح الأخطاء

يتضمن __tostring الجيد اسم النوع والحقول الأساسية. وبالنسبة إلى الكائنات المتداخلة، استدعِ tostring بصورة تكرارية. وهذا يجعل تصحيح الأخطاء أسهل بكثير من المخرجات الافتراضية مثل "table: 0x...".

local function tableToString(t, depth)
  depth = depth or 0
  if depth > 3 then return "{...}" end
  local parts = {}
  for k, v in pairs(t) do
    if type(v) == "table" then
      parts[#parts+1] = k.."="..tableToString(v, depth+1)
    else
      parts[#parts+1] = k.."="..tostring(v)
    end
  end
  return "{"..table.concat(parts,", ").."}"
end

local obj = {name="test", data={x=1,y=2}, active=true}
print(tableToString(obj))

__len مقابل العامل #

بالنسبة إلى الجداول التي لا تملك دالة ميتا __len، يبحث #t عن أي حد للتسلسل، حيث t[i] ~= nil وt[i+1] == nil. وعند وجود فجوات في الجدول، تكون النتيجة غير متوقعة. ويتيح __len تعريف طول موثوق ومتسق.

-- Table with hole: # is unpredictable
local t = {10, 20, nil, 40}
print(#t)  -- could be 2 or 4, undefined!

-- Custom container with authoritative length
local SafeList = {}
SafeList.__index = SafeList
SafeList.__len = function(s) return s._size end

function SafeList.new()
  return setmetatable({_data={}, _size=0}, SafeList)
end
function SafeList:add(v)
  self._size = self._size + 1
  self._data[self._size] = v
end
local s = SafeList.new()
s:add(1); s:add(nil); s:add(3)
print(#s)   -- 3 (always correct)

__tostring في رسائل الخطأ

عند استخدام كائنات الأخطاء، أي الجداول بدلًا من السلاسل النصية، يؤدي تعريف __tostring إلى جعل رسائل الخطأ مقروءة عندما ينتشر الخطأ ويُحوَّل إلى سلسلة نصية.

local function makeError(code, msg)
  return setmetatable(
    {code=code, message=msg},
    {__tostring = function(e)
      return string.format("Error[%d]: %s", e.code, e.message)
    end}
  )
end

local ok, err = pcall(function()
  error(makeError(404, "resource not found"))
end)

if not ok then
  -- err is the error object (table)
  print(tostring(err))  -- may show table address...

__tostring مع الوراثة

عند استخدام البرمجة كائنية التوجه القائمة على النماذج الأولية، عرّف __tostring في جدول ميتا الفئة. وستستخدم جميع المثيلات تلقائيًا دالة __tostring الخاصة بالفئة، لأن جدول ميتا المثيل هو جدول الفئة نفسه.

local Animal = {}
Animal.__index = Animal
Animal.__tostring = function(a)
  return string.format("%s(%s)", a._type or "Animal", a.name or "?")
end

function Animal.new(name, atype)
  return setmetatable({name=name, _type=atype}, Animal)
end

local cat = Animal.new("Whiskers", "Cat")
local dog = Animal.new("Rex", "Dog")
print(cat)  -- Cat(Whiskers)
print(dog)  -- Dog(Rex)

تنفيذ Set باستخدام __len و__tostring

ادمج __len و__tostring في نوع واحد للحصول على تنفيذ كامل وسهل الاستخدام لـ Set.

local Set = {}
Set.__index = Set
Set.__len = function(s) return s._size end
Set.__tostring = function(s)
  local items = {}
  for k in pairs(s._data) do items[#items+1]=tostring(k) end
  table.sort(items)
  return "Set{" .. table.concat(items,", ") .. "}"
end

function Set.new(...)
  local obj = setmetatable({_data={},_size=0}, Set)
  for _, v in ipairs({...}) do
    if not obj._data[v] then obj._data[v]=true; obj._size=obj._size+1 end
  end
  return obj
end

local s = Set.new(3,1,4,1,5,9,2,6)
print(#s)         -- 7 (duplicates removed)
print(tostring(s)) -- Set{1, 2, 3, 4, 5, 6, 9}

كائنات عددية باستخدام كليهما

أنشئ نوعًا للكسور يتيح __tostring عرضها، ويستخدم __len لعدد الأرقام أو لمقياس خاص بالمجال.

local Frac = {}
Frac.__index = Frac

local function gcd(a,b) return b==0 and a or gcd(b,a%b) end

function Frac.new(n,d)
  local g = gcd(math.abs(n), math.abs(d))
  return setmetatable({n=n//g, d=d//g}, Frac)
end

Frac.__tostring = function(f)
  if f.d==1 then return tostring(f.n) end
  return f.n .. "/" .. f.d
end

Frac.__add = function(a,b)
  return Frac.new(a.n*b.d + b.n*a.d, a.d*b.d)
end

local a = Frac.new(1,2)
local b = Frac.new(1,3)
print(a + b)   -- 5/6

تجاوز سلوك print الافتراضي

تستدعي Lua الدالة print الدالة tostring لكل معامل، مع الفصل بينها بعلامات تبويب. وعند تعريف __tostring، ستُعرض كائناتك بصورة مفيدة في أي تعليمة طباعة أو رسالة خطأ أو استيفاء سلسلة نصية يستخدم tostring.

local Color = {}
Color.__index = Color
Color.__tostring = function(c)
  return string.format("#%02X%02X%02X", c.r, c.g, c.b)
end

function Color.new(r,g,b)
  return setmetatable({r=r,g=g,b=b}, Color)
end

local red   = Color.new(255,0,0)
local green = Color.new(0,255,0)
local blue  = Color.new(0,0,255)

print(red, green, blue)
-- #FF0000  #00FF00  #0000FF

__name في Lua 5.3 والإصدارات الأحدث

تدعم Lua 5.3 والإصدارات الأحدث حقل __name في جدول الميتا، ويُستخدم في رسائل الخطأ الافتراضية لتحديد النوع. وعلى الرغم من أنه لا يؤثر مباشرةً في tostring()، فإنه يظهر في رسائل أخطاء وقت التشغيل عند إجراء عمليات غير صالحة على النوع.

local MyType = {}
MyType.__index = MyType
MyType.__name = "MyType"   -- used in error messages
MyType.__tostring = function(t)
  return "MyType(" .. tostring(t.value) .. ")"
end

local obj = setmetatable({value=42}, MyType)
print(obj)             -- MyType(42)

-- Error message will say "MyType" instead of "table"
-- pcall(function() local x = obj + 1 end)

تحقق سريع

متى تُستدعى __tostring؟

مراجعة: __tostring و__len

الملخص:

  • __tostring: تستدعيها tostring() وprint()
  • __len: يُستدعى بواسطة #obj؛ ويوفر طولًا موثوقًا للحاويات المخصصة
  • عرّف __tostring في جداول الفئات لجميع المثيلات
  • استخدم __len عندما لا تمنح بنية الجدول الخام قيمة صحيحة للعامل #
  • يسمّي __name (في 5.3 والإصدارات الأحدث) النوع في رسائل الخطأ

الأسئلة الشائعة

هل درس «‏__tostring و__len» مجاني؟

نعم — نص درس «‏__tostring و__len» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏__tostring و__len»؟

تحكّم في التمثيل النصي ومعامل # عبر الطرق الوصفية تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟

لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «‏__tostring و__len»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟

نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. مقدمة إلى جداول البيانات الوصفية
  2. ‏__index و__newindex
  3. الطرق الوصفية الحسابية
  4. ‏__tostring و__len
← العودة إلى Lua Academy