0Pricing
Lua Academy · บทเรียน

__tostring และ __len

ควบคุมการแสดงแทนข้อความและตัวดำเนินการ # ผ่านเมทาเมธอด

__tostring และ __len เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 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 ของคลาสโดยอัตโนมัติ เพราะเมตาเทเบิลของอินสแตนซ์คือ IS ตารางคลาส

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)

การสร้างเซตด้วย __len และ __tostring

รวม __len และ __tostring ไว้ในชนิดข้อมูลเดียว เพื่อสร้างการใช้งานเซตที่สมบูรณ์และใช้งานง่าย

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

print ของ Lua จะเรียก 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” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “__tostring และ __len”

ควบคุมการแสดงแทนข้อความและตัวดำเนินการ # ผ่านเมทาเมธอด คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “__tostring และ __len” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม

ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รู้จักเมทาเทเบิล
  2. __index และ __newindex
  3. เมทาเมธอดทางเลขคณิต
  4. __tostring และ __len
← กลับไปที่ Lua Academy