เมตาตาราง

*เนื้อหานี้แปลโดยใช้ AI (เวอร์ชัน Beta) และอาจมีข้อผิดพลาด หากต้องการดูหน้านี้เป็นภาษาอังกฤษ ให้คลิกที่นี่

เมตาตารางทำให้ตารางมีความสามารถมากขึ้นกว่าเดิม พวกมันถูกแนบกับข้อมูลและมีค่าที่เรียกว่าเมธอดเมตา เมธอดเมตาจะถูกเรียกเมื่อมีการใช้การกระทำบางอย่างกับข้อมูลที่มันแนบอยู่

จัดการเมตาตาราง

ฟังก์ชันหลักสองฟังก์ชันสำหรับการเพิ่มและค้นหาเมตาตารางของตารางคือ 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ใช้ในตารางอ่อน โดยประกาศว่าคีย์และ/หรือค่าของตารางเป็นอ่อน ดู ใช้ตารางอ่อน.
__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 do
print(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]) -- ทำให้เกิด stack overflow

การเกิด stack overflow จะเกิดขึ้นเมื่อคุณพยายามเรียกฟังก์ชันจากตัวมันเองมากเกินไป ในฟังก์ชัน __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

ใช้ตารางอ่อน

ตัวแปรที่ถือว่าตาราง ฟังก์ชัน เธรด และ ยูเซอร์ดาต้า รวมถึง Instances จะเก็บอ้างอิงไปยังวัตถุเหล่านั้นแทนที่จะเป็นสำเนาของมัน การกำหนดวัตถุให้กับตัวแปรอื่นจะสร้างอ้างอิงอีกอันไปยังวัตถุเดียวกัน:

local map = {key = 1}
local mapReference = map
print(map == mapReference) --> true
print(map == {key = 1}) --> false
local function func() end
local funcReference = func
print(func == funcReference) --> true
local part = Instance.new("Part")
local partReference = part
print(part == partReference) --> true

วัตถุที่ไม่สามารถเข้าถึงได้ผ่านอ้างอิงที่แข็งแกร่งจะกลายเป็นที่เหมาะสมสำหรับการเก็บขยะ การตั้งค่าตัวแปรหนึ่งเป็น nil จะไม่ทำให้วัตถุเหมาะสมสำหรับการเก็บขยะหากอ้างอิงที่แข็งแกร่งอื่นยังชี้ไปยังวัตถุเดียวกัน:

local child = {}
local parent = {Child = child}
child = nil
print(parent.Child) --> table: [hexadecimal memory address]

แม้ว่า child จะไม่ชี้ไปยังตารางลูกอีกต่อไป แต่ parent.Child ยังคงชี้ไปยังมัน ตราบใดที่ตารางพ่อแม่ยังสามารถเข้าถึงได้ อ้างอิงของมันจะทำให้ตารางลูกยังสามารถเข้าถึงได้เช่นกัน

ตารางอ่อน จะถือคีย์ ค่าหรือทั้งสองอย่างโดยไม่ป้องกันไม่ให้การเก็บขยะสามารถเรียกคืนวัตถุที่ไม่สามารถเข้าถึงได้อีกต่อไป ตั้งค่าเขตข้อมูล __mode ของเมตาตารางของตารางเพื่อควบคุมว่าการอ้างอิงใดเป็นอ่อน:

  • "k" ทำให้คีย์เป็นอ่อน
  • "v" ทำให้ค่าเป็นอ่อน
  • "kv" หรือ "vk" ทำให้ทั้งคีย์และค่าเป็นอ่อน

ตัวอย่างโค้ดต่อไปนี้สร้างตารางที่มีค่าที่อ่อน:

local children = setmetatable({}, {
__mode = "v",
})
do
local child = {}
children.example = child
end

หลังจากบล็อก do สิ้นสุด ค่าที่เก็บอยู่ใน children.example จะไม่มีการอ้างอิงที่แข็งแกร่งและกลายเป็นที่เหมาะสมสำหรับการเก็บขยะ เมื่อการเก็บขยะเรียกคืนตารางลูก Luau จะลบรายการของมันออกจาก children การเก็บขยะเป็นกระบวนการที่ไม่สามารถคาดเดาได้ ดังนั้นอย่าพึ่งพาว่ารายการจะหายไปเมื่อใด

โหมดใด ๆ ที่คุณไม่ระบุจะยังคงเป็นอ้างอิงที่แข็งแกร่ง คีย์ในตาราง __mode = "v" ยังคงเป็นการอ้างอิงที่แข็งแกร่ง ดังนั้นวัตถุที่เก็บไว้เป็นคีย์จะยังคงสามารถเข้าถึงได้และรายการของมันจะยังคงอยู่ในตาราง

สำหรับ Instances การเก็บขยะจะใช้กับการอ้างอิง Luau แทนที่จะเป็นวัตถุเอง เอนจินจะเก็บข้อมูลอินสแตนซ์ไว้ภายนอก VM ของ Luau ดังนั้นการเก็บอ้างอิง Luau จะไม่ลบอินสแตนซ์ และอินสแตนซ์ที่ยังคงอยู่ในโมเดลข้อมูลจะไม่ทำให้การอ้างอิง Luau ของมันยังคงมีชีวิตอยู่ เพื่อปล่อยอินสแตนซ์ ให้เรียก Instance:Destroy() และลบการอ้างอิงที่เหลืออยู่ทั้งหมดออก

ใช้ประเภทเซ็ต

เซ็ต คือการรวบรวมของรายการที่ไม่มีลำดับและไม่มีองค์ประกอบที่ซ้ำกัน รายการหนึ่ง มี หรือ ไม่มี อยู่ในเซ็ต โดยใช้เมตาตาราง คุณสามารถสร้างและจัดการเซ็ตภายในสคริปต์ 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({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})

โปรดทราบว่าตามคำนิยาม เซ็ตไม่มีแนวคิดเรื่องการจัดลำดับ

เพิ่มรายการ

การเพิ่มรายการลงในเซ็ตที่มีอยู่สามารถทำได้ผ่านวิธี Set:add()

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:add("Mango")

ลบรายการ

ในการลบรายการออกจากเซ็ต ให้เรียก Set:remove() พร้อมกับชื่อรายการ

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:remove("Orange")

ตรวจสอบรายการ

ในการตรวจสอบว่าเซ็ตมีรายการเฉพาะหรือไม่ ให้ใช้ Set:contains()

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local result1 = fruits:contains("Cherry")
print(result1) -- true
local result2 = fruits:contains("Watermelon")
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({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach

การรวม

คุณสามารถรับ การรวม ของสองเซ็ตด้วยฟังก์ชันต่อไปนี้ หมายถึงการรวบรวมรายการในทั้งสองเซ็ตโดยไม่มีรายการซ้ำ โปรดทราบว่าฟังก์ชันนี้ใช้เมตาตาราง __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({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- Peach, Lime, Apple, Cherry, Lemon, Mango

การหักลบ

คุณสามารถลบรายการทั้งหมดในเซ็ตหนึ่งจากรายการในเซ็ตอื่นผ่านฟังก์ชันต่อไปนี้ คล้ายกับฟังก์ชันข้างต้น ฟังก์ชันนี้ใช้เมตาตาราง __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({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach
©2026 Roblox Corporation. Roblox, โลโก้ Roblox และ Powering Imagination เป็นส่วนหนึ่งของเครื่องหมายการค้าที่จดทะเบียน และไม่ได้จดทะเบียนของเราในสหรัฐฯ และประเทศอื่นๆ