Metabel

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

Metatables memungkinkan tabel menjadi lebih kuat daripada sebelumnya. Mereka ditautkan ke data dan berisi nilai yang disebut metamethode. Metamethode diaktifkan saat tindakan tertentu digunakan dengan datum yang ditautkannya.

Pertimbangkan kode berikut:


local list = {1, 2}
print(list[3])

Anda mungkin mengharapkan kode ini untuk mencari melalui daftar untuk indeks ketiga dalam daftar, menemukan apa-apa, dan kemudian mengembalikan nil. Itu tidak benar, though. Apa yang sebenarnya terjadi adalah kode mencari melalui daftar untuk indeks ketiga, menemukan apa-apa, dan kemudian memeriksa apakah ada metabel yang ditautkan ke tabel, mengembalikan nil jika tidak ada satu.

Mengelola Metatabel

Dua fungsi utama untuk menambahkan dan menemukan tabel metabel, adalah setmetatable dan getmetatable


local x = {}
local metaTable = {} -- metaTables adalah tabel, juga!
setmetatable(x, metaTable) -- Berikan x meja yang bernama metaTable!
print(getmetatable(x)) --> table: [hexadecimal memory address]

Fungsi setmetatable juga mengembalikan tabel yang Anda tetapkan metabel, jadi kedua script ini melakukan hal yang sama:


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

local x = setmetatable({}, {})

Metode Metam

Metamethode adalah fungsi yang tersimpan di dalam metabel. Mereka dapat pergi dari memanggil tabel, menambahkan tabel, bahkan membagi tabel juga. Berikut adalah daftar metamethode yang tersedia:

MetodeDeskripsi
__index(meja, indeks)Dibakar saat tabel[index] diindeks, jika tabel[index] nol. Bisa juga diatur ke tabel, di mana tabel itu akan diindeks.
__newindex(tabel, indeks, nilai)Diaktifkan saat tabel[index] mencoba untuk ditetapkan (tabel[index] = nil), jika tabel[index] nol. Bisa juga diatur ke tabel, di mana tabel itu akan diindeks.
__call(table, ...)Dibakar saat tabel dianggap seperti fungsi, ... adalah argument yang dilewati.
__concat(meja, nilai)Dibakar ketika operator .. concatenation digunakan di tabel.
__unm(tabel)Diaktifkan ketika operator unary - digunakan di tabel.
__add(table, value)Operator +.
__sub(table, value)The - operator pengurangan.
__mul(tabel, nilai)Operator * multiplikasi.
__div(meja, nilai)Operator / divisi.
__idiv(meja, nilai)The // divisi floor.
__mod(meja, nilai)Operator modul %
__pow(meja, nilai)^ operator eksponensi.
__tostring(tabel)Diaktifkan saat tostring dinyetik di tabel.
__metabelJika ada, mengunci tabel metabel sehingga getmetabel akan mengembalikan ini alih-alih tabel metabel dan setmetabel akan error. Non-Function value.
__eq(meja, nilai)The == sama dengan operator¹
__lt(tabel, nilai)The < kurang dari operator¹
__le(tabel, nilai)The Operator¹
modeDigunakan di tabel yang lemah, menyatakan apakah kunci dan/atau nilai tabel lemah. Catatan: Rujukan ke instansi Roblox tidak pernah lemah. Tabel yang menyimpan rujukan seperti itu tidak akan pernah dikumpulkan sampah.
__gc(meja)Diaktifkan saat tabel diumpah. Catatan: Di Roblox, metode metametode ini dinonaktifkan.
__len(tabel)Diaktifkan ketika operator panjang # digunakan pada Objek.
__iter(tabel)Digunakan untuk menyebutkan iterator khusus saat menggunakan iterator umum.

Harus dicatat bahwa ketika menulis fungsi untuk metametode aritmatik atau relasional, dua parameter fungsi dapat bertukar antara tabel yang mengeksekusi metametode dan tabel lainnya. Misalnya, ketika melakukan operasi vektor dengan divisi skala tidak bersifat komutif. Oleh karena itu, jika Anda menulis metametode untuk kelas vektor Anda sendiri, Anda harus berhati-


local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a adalah skala, b adalah vektor
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a adalah vektor, b adalah skala
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
-- kedu a dan b adalah vektor
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.333333333333, 1.25
print(b / a) -- kurang dari 0,3, 0,8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5

Menggunakan Metatables

Ada banyak cara untuk menggunakan metatables, misalnya metamethode __unm (untuk membuat tabel negatif):


local metatable = {
__unm = function(t) -- __unm adalah untuk operator - unary
local negated = {}
for key, value in t do
negated[key] = -value -- negat semua nilai di tabel ini
end
return negated -- kembali tabel
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Inilah cara yang menarik untuk menyatakan hal-hal menggunakan __index :


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

__index dihentikan ketika x diindeks di tabel dan tidak ditemukan. Lua kemudian mencari melalui tabel __index untuk mencari indeks bernama x, dan menemukan satu, kemudian mengembalikan itu.

Sekarang Anda dapat dengan mudah melakukan itu dengan fungsi sederhana, tetapi ada banyak lagi di mana itu berasal. Ambil ini sebagai contoh:


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

Sekarang, tentu saja Anda tidak bisa memanggil tabel. Itu hanya gila, tapi (kejutan, kejutan!) dengan metatabel Anda bisa.


local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Tambahkan argument (5) ke value, lalu letakkan di tabel baru (t).
end
return unpack(sum) -- Kembalikan nilai tabel individual
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Anda dapat melakukan lebih banyak hal juga, seperti menambahkan tabel!


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

Ini akan mengalami kesalahan mengatakan bahwa Anda mencoba melakukan aritmetik pada tabel. Mari coba ini dengan metabel.


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

Gunakan Kasus

Sekarang, semua contoh ini dapat dilaksanakan sebagai fungsi sederhana, tetapi Anda dapat melakukan lebih banyak daripada itu. Mari coba program sederhana yang akan menyimpan angka ketika mungkin masalah matematika laggy ditambahkan ke dalamnya.

Untuk yang satu ini kita akan menggunakan metode metametam __index hanya untuk membuatnya sederhana:


local function mathProblem(num)
for i = 1, 20 do
num = math.floor(num * 10 + 65)
end
for i = 1, 10 do
num += i - 1
end
return num
end
local metatable = {
__index = function(object, key)
local num = mathProblem(key)
object[key] = num
return num
end
}
local t = setmetatable({}, metatable)
print(t[1]) -- Akan lambat karena ini adalah pertama kalinya menggunakan nomor ini, jadi harus mengeksekusi fungsi matematika.
print(t[2]) -- akan lambat karena ini adalah pertama kalinya menggunakan nomor ini.
print(t[1]) -- will be fast because it's just grabbing the number from the table.

Rawset, Rawget, Rawequal

Ketika bermain dengan metatables, Anda mungkin menghadapi beberapa masalah. Apa yang terjadi jika Anda perlu menggunakan metamethode __index metamethode untuk menciptakan nilai baru di tabel, tetapi metamethode mete tabel juga memiliki metamethode __newindex


local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- hanya sebagai contoh
return self[i]
end,
__newindex = function(self, i, v)
--jangan lakukan apa pun karena kami tidak ingin anda menetapkan nilai ke tabel dengan cara normal
end
})
print(t[1]) -- Causes a C-Stack overflow

Sekarang mengapa itu menyebabkan overrun stack? Overrun stack terjadi ketika Anda mencoba untuk memanggil fungsi dari dirinya sendiri terlalu banyak kali, tetapi apa yang akan menyebabkan itu terjadi? Dalam fungsi self[i], kami menetapkan self[i] ke nil

Masalahnya adalah bahwa __newindex tidak memungkinkan kita menetapkan nilainya. Kehadirannya menghentikan nilai dari ditambahkan ke tabel dengan metode standar t[i] = v. Untuk mengatasi ini, Anda menggunakan fungsi rawset.


local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
--jangan lakukan apa pun karena kami tidak ingin anda menetapkan nilai ke tabel dengan cara normal
end
})
print(t[1]) -- prints 10

Menggunakan Set Datatype

Sebuah set adalah koleksi item tanpa urutan dan tanpa elemen duplikasi. Sebuah item adalah atau bukan terkandung dalam sebuah atur. Menggunakan metatabel, Anda dapat mengkonstruksi dan menyetel set dalam Lua script.

Metode Dasar

Kode berikut mencakup kesetelan dasar, memungkinkan Anda membangun set baru, menambahkan dan menghapus item, memeriksa apakah set berisi item, dan mengekspor konten atur.


local Set = {}
Set.__index = Set
-- Fungsi untuk membangun set dari daftar item yang opsional
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Fungsi untuk menambahkan item ke atur
function Set:add(item)
self[item] = true
end
-- Fungsi untuk menghapus item dari atur
function Set:remove(item)
self[item] = nil
end
-- Fungsi untuk memeriksa apakah set berisi item
function Set:contains(item)
return self[item] == true
end
-- Fungsi untuk mengekspor set sebagai daftar koma-delimited untuk debug
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Buat Set

Sebuah set baru dapat dibangun dengan memanggil Set.new() dengan array item pilihan untuk ditambahkan.


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

Catat bahwa secara definisi, set tidak memiliki konsep memesan.

Tambahkan Item

Menambahkan item ke set yang sudah ada dapat dilakukan melalui metode Set:add() .


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

Hapus Item

Untuk menghapus item dari atur, panggil Set:remove() dengan nama item.


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

Periksa untuk Item

Untuk memeriksa apakah set berisi item spesifik, gunakan Set:contains() .


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

Metode tambahan

Operasi yang berguna lainnya dapat dilaksanakan untuk set, memungkinkan Anda untuk membandingkan item di antara set, menggabungkan set, atau mengurangkan satu set dari set lain.

Persimpangan

Ketika menganggap set sebagai grafik Venn, Anda dapat mendapatkan persimpangan dari dua set sebagai berikut, yang berarti item yang muncul di setiap set.


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

Persatuan

Anda dapat mendapatkan persatuan dari dua set dengan fungsi berikut, yang berarti koleksi item di kedua set tanpa duplikat. Catat bahwa fungsi ini menggunakan metabel __add untuk memberikan jalan pintas tambahan dari 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

Penghapusan

Anda dapat menghapus semua item dalam satu set dari item dalam set lain melalui fungsi berikut. Mirip dengan fungsi di atas, ini menggunakan metabel __sub metode untuk memberikan jalan pintas subtraksi 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