Bảng biểu Metatables

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

Bảng biểu cho phép các bảng trở nên mạnh mẽ hơn trước.Chúng được gắn vào dữ liệu và chứa các giá trị được gọi là metamethods.Metamethods được kích hoạt khi một hành động nhất định được sử dụng với datum mà nó được gắn kết.

Thao tác bảng meta

Hai chức năng chính để thêm và tìm kiếm metatable của bảng là setmetatable()getmetatable() .


local x = {}
local metaTable = {} -- Bảng metaTables cũng là bảng, nữa!
setmetatable(x, metaTable) -- Cung cấp x một bảng có thể gọi là bảng metaTable!
print(getmetatable(x)) --> table: [hexadecimal memory address]

Chức năng setmetatable() cũng trả về bảng mà bạn đang đặt metatable, vì vậy hai kịch bản này làm điều tương tự:


local x = {}
setmetatable(x, {})

local x = setmetatable({}, {})

Phương pháp Metamethods

Metamethods là các chức năng được lưu bên trong một metatable.Họ có thể đi từ việc gọi một bảng, đến thêm một bảng, thậm chí là chia nhỏ bảng nữa.Đây là danh sách các metamethods có sẵn:

Phương phápMô tả
__index(table, index)Bắt lửa khi table[index] được chỉ mục hóa, nếu table[index]nil . Cũng có thể được đặt thành một bảng, trong đó bảng đó sẽ được chỉ mục hóa.
__newindex(table, index, value)Bắt lửa khi table[index] đang cố gắng được đặt (table[index] = value) , nếu table[index]nil .Cũng có thể được đặt thành một bảng, trong đó bảng đó sẽ được chỉ mục hóa.
__call(table, ...)Bắt lửa khi bảng được gọi như một chức năng, ... là những tham số đã được truyền.
__concat(table, value)Bắt lửa khi operator kết hợp .. được sử dụng trên bảng.
__unm(table)Bắt lửa khi operator unary được sử dụng trên bảng.
__add(table, value)Nhà vật lý + Nhà vật lý.
__sub(table, value)Hoạt động trừ .
__mul(table, value)Hoạt động nhân * nhân.
__div(table, value)Nhà phân phối / chia sẻ.
__idiv(table, value)Người phân chia sàn // .
__mod(table, value)Người vận hành modulus % .
__pow(table, value)Hoạt động nhân số ^ .
__tostring(table)Bị sa thải khi tostring được gọi trên bảng.
__metatableNếu có, khóa metatable để getmetatable() sẽ trả về giá trị này thay vì metatable và setmetatable() sẽ lỗi. Giá trị không chức năng.
__eq(table, value)Các đối tượng == bằng với operator¹
__lt(table, value)Vận hành < ít hơn so với operator¹
__le(table, value)Nhà vận hành <= ¹
__modeSử dụng trong các bảng yếu, tuyên bố xem liệu các chìa khóa và/hoặc giá trị của một bảng có yếu hay không.Lưu ý rằng các tham chiếu đến các ví dụ Roblox không bao giờ yếu.Các bảng chứa các tham chiếu như vậy sẽ không bao giờ được thu thập rác.
__len(table)Bị sa thải khi operator dài # được sử dụng trên đối tượng.
__iter(table)Dùng để chỉ một iterator tùy chỉnh khi sử dụng lặp lại hóa hóa.

Cần lưu ý rằng khi viết chức năng cho cả phép toán hoặc phương pháp biến đổi quan hệ, các tham số hai chức năng có thể thay thế lẫn nhau giữa bảng bắn metamethod và giá trị khác.Ví dụ, khi thực hiện các hoạt động vector với phân chia scalar không là hợp nhất.Do đó, nếu bạn đang viết metamethods cho lớp riêng của mình vector2, bạn sẽ muốn cẩn thận để tính toán cho cả hai kịch bản.


local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a là một số thực, b là một vector
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a là một vector, b là một scalar
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
-- cả a và b là vector
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

Sử dụng

Có nhiều cách để sử dụng bảng biểu, ví dụ như __unm metamethod để làm cho một bảng trở nên tiêu cực:


local metatable = {
__unm = function(t) -- __unm là cho phép tính toán unary - operator
local negated = {}
for key, value in t do
negated[key] = -value -- phủ nhận tất cả các giá trị trong bảng này
end
return negated -- trả lại bảng
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Đây là một cách thú vị để tuyên bố các thứ bằng cách sử dụng __index :


local metatable = {
__index = {x = 1}
}
local t = setmetatable({}, metatable)
print(t.x) --> 1

__index đã bị bắn khi x được chỉ mục hóa trong bảng và không tìm thấy.Luau sau đó tìm kiếm thông qua bảng __index để tìm một chỉ mục có tên là x , và, tìm thấy một, trả lại nó.

Bây giờ bạn có thể dễ dàng làm điều đó với một chức năng đơn giản, nhưng có nhiều thứ hơn nữa từ đó xuất phát. Hãy lấy ví dụ này:


local t = {10, 20, 30}
print(t(5))

Thông thường bạn không thể gọi một bảng, nhưng với metatables bạn có thể:


local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Thêm argument (5) vào giá trị, sau đó đặt nó vào bảng mới (t).
end
return unpack(sum) -- Trả lại các giá trị bảng cá nhân
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Bạn cũng có thể làm nhiều hơn nữa, chẳng hạn như thêm bảng:


local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
for k, v in table1 + table2 do
print(k, v)
end

Lỗi này sẽ xảy ra khi bạn cố gắng thực hiện phép tính trên một bảng, nhưng nó hoạt động khi thử với một metatable:


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

Khi chơi với bảng biểu, bạn có thể gặp phải một số vấn đề.Nếu bạn cần sử dụng metamethod __index để tạo các giá trị mới trong một bảng, nhưng bảng metatable cũng có một metamethod __newindex metamethod, bạn sẽ muốn sử dụng chức năng Luau tích hợp sẵn rawset() để đặt giá trị mà không kích hoạt bất kỳ metamethod nào.Lấy mã sau đây là ví dụ về những gì xảy ra nếu bạn không sử dụng các chức năng này.


local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Không đặt giá trị cho bảng theo cách thông thường
end
})
print(t[1]) -- Causes a stack overflow

Tình trạng bộ nhớ đệp xảy ra khi bạn cố gọi một chức năng từ chính nó quá nhiều lần.Trong chức năng __index ở trên, self[i] được đặt thành một giá trị, vì vậy khi nó đến đến dòng tiếp theo, self[i] nên tồn tại và có lẽ sẽ không gọi đến phương pháp metamethod __index.Vấn đề là __newindex không cho phép bạn đặt giá trị.Sự hiện diện của nó ngăn các giá trị được thêm vào bảng bằng cách sử dụng phương pháp tiêu chuẩn t[i] = v.Để vượt qua điều này, hãy sử dụng chức năng rawset():


local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Không đặt giá trị cho bảng theo cách thông thường
end
})
print(t[1]) --> 10

Sử dụng loại dữ liệu được đặt

Một bộ sưu tập là một bộ sưu tập các vật phẩm không có trật tự và không có các yếu tố trùng lặp.Một mục hoặc hoặc không nằm trong một cài đặt.Sử dụng bảng biểu, bạn có thể xây dựng và thao tác với các bộ trong các kịch bản Luau.

Phương pháp cơ bản

Mã sau đây bao gồm chức năng cơ bản của bộ, cho phép bạn xây dựng các bộ mới, thêm và xóa một vật phẩm, kiểm tra xem bộ có chứa một vật phẩm hay không, và xuất nội dung của một cài đặt.


local Set = {}
Set.__index = Set
-- Chức năng để xây dựng một bộ từ một danh sách tùy chọn các mục
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Chức năng thêm một mục vào một cài đặt
function Set:add(item)
self[item] = true
end
-- Chức năng để xóa một mục khỏi một cài đặt
function Set:remove(item)
self[item] = nil
end
-- Chức năng kiểm tra xem một bộ có chứa một vật phẩm không
function Set:contains(item)
return self[item] == true
end
-- Chức năng xuất bộ như một danh sách có phân cách bằng dấu phẩy để gỡ lỗi
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Tạo cài đặt

Một bộ mới có thể được xây dựng bằng cách gọi Set.new() với một mảng bắt buộc của các mục để thêm.


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

Lưu ý rằng theo định nghĩa, một bộ không có khái niệm về trật tự.

Thêm vật phẩm

Thêm một mục vào một bộ hiện có có thể được thực hiện thông qua phương pháp Set:add().


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

Loại bỏ vật phẩm

Để xóa một mục khỏi một cài đặt, gọi Set:remove() với tên mục.


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

Kiểm tra vật phẩm

Để kiểm tra xem một bộ có chứa một vật phẩmcụ thể hay không, hãy sử dụng Set:contains() .


local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local result1 = fruits:contains("Cherry")
print(result1) -- đúng
local result2 = fruits:contains("Watermelon")
print(result2) -- false

Phương pháp bổ sung

Các hoạt động hữu ích khác có thể được thực hiện cho các bộ, cho phép bạn so sánh các mục giữa các bộ, kết hợp các bộ hoặc trừ một bộ khỏi một bộ khác.

Giao lộ

Khi xem xét các bộ như đồ thị Venn, bạn có thể nhận được điểm giao nhau của hai bộ như sau, có nghĩa là các mục xuất hiện trong cả hai bộ .


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

Liên minh

Bạn có thể nhận được liên minh của hai bộ với chức năng sau, có nghĩa là một bộ sưu tập các vật phẩm trong cả hai bộ mà không có trùng lặp.Lưu ý rằng chức năng này sử dụng phương pháp metatable __add để cung cấp một lối tắt bổ sung của 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() -- Xoài, Chanh, Táo, Cherry, Chanh, Xoài

Trừ bớt

Bạn có thể xóa tất cả các mục trong một bộ từ các mục trong bộ khác thông qua chức năng sau.Tương tự như chức năng trên, cái này sử dụng phương pháp metatable __sub để cung cấp một lối tắt trừ 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