Metatable'lar, tabloların daha güçlü hale gelmesini sağlar. Verilere eklenirler ve metametotlar adı verilen değerler içerirler. Metametotlar, belirli bir eylem, ilişkilendirildiği veri ile kullanıldığında tetiklenir.
Metatable'ları Manipüle Etme
Bir tablonun metatable'ını eklemek ve bulmak için iki ana fonksiyon setmetatable() ve getmetatable()'dır.
local x = {}
local metaTable = {} -- Metatable'lar da tablodur!
setmetatable(x, metaTable) -- x'e metaTable adında bir metatable ver!
print(getmetatable(x)) --> table: [hexadecimal memory address]setmetatable() fonksiyonu, metatable'ını ayarladığınız tabloyu da döndürdüğü için, bu iki script aynı şeyi yapar:
local x = {}
setmetatable(x, {})local x = setmetatable({}, {})Metametotlar
Metametotlar, bir metatable içinde saklanan fonksiyonlardır. Bir tablonun çağrılmasından, bir tablo eklenmesine, hatta tabloların bölünmesine kadar gidebilirler. İşte mevcut metametotların listesi:
| Metot | Açıklama |
|---|---|
| __index(table, index) | table[index] dizinlendiğinde tetiklenir, eğer table[index] nil ise. Ayrıca bir tabloya ayarlanabilir, bu durumda o tablo dizinlenecektir. |
| __newindex(table, index, value) | table[index] ayarlanmaya çalışıldığında tetiklenir (table[index] = value), eğer table[index] nil ise. Ayrıca bir tabloya ayarlanabilir, bu durumda o tablo dizinlenecektir. |
| __call(table, ...) | Tablo bir fonksiyon gibi çağrıldığında tetiklenir, ... geçilen argümanlardır. |
| __concat(table, value) | Tablo üzerinde .. birleştirme operatörü kullanıldığında tetiklenir. |
| __unm(table) | Tablo üzerinde unary – operatörü kullanıldığında tetiklenir. |
| __add(table, value) | + toplama operatörü. |
| __sub(table, value) | – çıkarma operatörü. |
| __mul(table, value) | * çarpma operatörü. |
| __div(table, value) | / bölme operatörü. |
| __idiv(table, value) | // tam bölme operatörü. |
| __mod(table, value) | % modül operatörü. |
| __pow(table, value) | ^ üstel operatör. |
| __tostring(table) | Tablo üzerinde tostring çağrıldığında tetiklenir. |
| __metatable | Varlığı durumunda, metatable'ı kilitler, böylece getmetatable() bu değeri döndürür ve setmetatable() hata verir. Fonksiyon olmayan bir değer. |
| __eq(table, value) | == eşitlik operatörü¹ |
| __lt(table, value) | < küçüktür operatörü¹ |
| __le(table, value) | <= operatörü¹ |
| __mode | Zayıf tablolarda kullanılır, bir tablonun anahtarlarının ve/veya değerlerinin zayıf olup olmadığını belirtir. Zayıf tabloları kullanın. |
| __len(table) | # uzunluk operatörü kullanıldığında tetiklenir. |
| __iter(table) | Genelleştirilmiş yineleme kullanırken özel bir yineleyici belirtmek için kullanılır. |
Fonksiyonlar yazarken, ister aritmetik ister ilişkisel metametotlar için, iki fonksiyon parametresi, metametodu tetikleyen tablo ile diğer değer arasında değiştirilebilir. Örneğin, skalarlarla vektör işlemleri yaparken bölme işlemi komütatif değildir. Bu nedenle, kendi vector2 sınıfınız için metametotlar yazıyorsanız, her iki durumu da hesaba katmak için dikkatli olmalısınız.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a bir skalar, b bir vektör
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a bir vektör, b bir skalar
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
-- hem a hem de b vektörler
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.5Kullanım
Metatable'ları kullanmanın birçok yolu vardır.
Tablolara unary eksi operatörünü uygulama
__unm metametodu unary eksi operatörünü işler. Bir tablodaki her değeri negatif hale getirmek için kullanın:
local metatable = {
__unm = function(t) -- __unm unary - operatörü içindir
local negated = {}
for key, value in t do
negated[key] = -value -- bu tablodaki tüm değerleri negatif hale getir
end
return negated -- tabloyu döndür
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12Diğer bir tablodan eksik değerleri dizinleme
__index kullanarak şeyleri tanımlamanın ilginç bir yolu:
local metatable = {
__index = {x = 1}
}
local t = setmetatable({}, metatable)
print(t.x) --> 1x tablodaki dizinlenirken __index tetiklendi ve bulunamadı. Luau, __index tablosunda x adında bir dizin aradı ve bulduğunda onu döndürdü.
Bir tabloyu fonksiyon gibi çağırma
Metatable'lar, eksik bir değeri sağlamaktan çok daha fazlasını yapabilir. Örneğin:
local t = {10, 20, 30}
print(t(5))Genellikle bir tabloyu çağırmak mümkün değildir, ancak metatable'lar ile bunu yapabilirsiniz:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Argümanı (5) değere ekleyin, ardından yeni tabloya (t) yerleştirin.
end
return unpack(sum) -- Bireysel tablo değerlerini döndür
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35Tablolara toplama operatörünü uygulama
Ayrıca tabloları toplamak gibi çok daha fazlasını yapabilirsiniz:
local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
for k, v in table1 + table2 do
print(k, v)
endBu, bir tablo üzerinde aritmetik yapmaya çalıştığınız için hata verecektir, ancak bir metatable ile denendiğinde çalışır:
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)
endMetametotları tetiklemeksizin değerleri ayarlama
Metatable'larla oynarken bazı sorunlarla karşılaşabilirsiniz. Eğer __index metametodunu kullanarak bir tabloda yeni değerler oluşturmanız gerekiyorsa, ancak o tablonun metatable'ında da bir __newindex metametodu varsa, değeri herhangi bir metametodu tetiklemeksizin ayarlamak için Luau'nun yerleşik fonksiyonu rawset()'i kullanmak isteyeceksiniz. Aşağıdaki kod, bu fonksiyonları kullanmadığınızda ne olacağını gösterir.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Değerleri tabloya normal şekilde ayarlamayın
end
})
print(t[1]) -- Yığın taşması oluştururYığın taşmaları, bir fonksiyonu kendisinden çok fazla kez çağırmaya çalıştığınızda meydana gelir. Yukarıdaki __index fonksiyonunda, self[i] bir değere ayarlandığında, bir sonraki satıra geldiğinde self[i] var olmalı ve muhtemelen __index metametodunu çağırmamalıdır. Sorun, __newindex'in değeri ayarlamanıza izin vermemesidir. Varlığı, standart t[i] = v yöntemiyle tabloya değer eklemeyi durdurur. Bunu aşmak için rawset() fonksiyonunu kullanın:
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Değerleri tabloya normal şekilde ayarlamayın
end
})
print(t[1]) --> 10Zayıf Tabloları Kullanma
Tabloları, fonksiyonları, iş parçacıklarını ve kullanıcı verilerini, Instances dahil olmak üzere, bu nesnelerin kopyalarını değil, referanslarını saklar. Bir nesneyi başka bir değişkene atamak, aynı nesneye başka bir referans oluşturur:
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) --> trueArtık güçlü referanslar aracılığıyla erişilemeyen nesneler, çöp toplama için uygun hale gelir. Bir değişkeni nil olarak ayarlamak, başka bir güçlü referans hala o nesneye işaret ediyorsa, o nesneyi çöp toplama için uygun hale getirmez:
local child = {}
local parent = {Child = child}
child = nil
print(parent.Child) --> table: [hexadecimal memory address]child artık çocuk tablosuna referans vermediği halde, parent.Child hala bunu yapmaktadır. Ana tablo erişilebilir olduğu sürece, referansı çocuk tablosunu da erişilebilir kılar.
Bir zayıf tablo, anahtarları, değerleri veya her ikisini de, aksi takdirde erişilemeyen nesnelerin çöp toplayıcı tarafından geri alınmasını engellemeden saklar. Tablo metatable'ının __mode alanını ayarlayarak hangi referansların zayıf olduğunu kontrol edebilirsiniz:
- "k" anahtarları zayıf yapar.
- "v" değerleri zayıf yapar.
- "kv" veya "vk" hem anahtarları hem de değerleri zayıf yapar.
Aşağıdaki kod örneği, zayıf değerlere sahip bir tablo oluşturur:
local children = setmetatable({}, {
__mode = "v",
})
do
local child = {}
children.example = child
enddo bloğu sona erdikten sonra, children.example içinde saklanan değer güçlü referanslara sahip değildir ve çöp toplama için uygun hale gelir. Çöp toplayıcı çocuk tablosunu geri aldığında, Luau onun children içindeki kaydını kaldırır. Çöp toplama belirsizdir, bu nedenle kaydın ne zaman kaybolduğuna güvenmeyin.
Bıraktığınız herhangi bir mod güçlü kalır. __mode = "v" tablosundaki anahtarlar güçlü referanslar olarak kalır, bu nedenle bir anahtar olarak saklanan bir nesne erişilebilir kalır ve kaydı tabloda kalır.
Instances için çöp toplama, Luau referansına uygulanır, nesnenin kendisine değil. Motor, örnek verilerini Luau VM dışına saklar, bu nedenle bir Luau referansını toplamak, örneği silmez ve veri modelinde kalan bir örnek, Luau referansını canlı tutmaz. Bir örneği serbest bırakmak için Instance:Destroy() çağırın ve ona kalan tüm referansları temizleyin.
Set Veri Tipini Kullanma
Bir set, sırasız ve tekrarsız öğelerden oluşan bir koleksiyondur. Bir öğe ya vardır ya da yoktur bir set içinde. Metatable'ları kullanarak, Luau scriptleri içinde setler oluşturabilir ve manipüle edebilirsiniz.
Temel Metotlar
Aşağıdaki kod, yeni setler oluşturmanıza, bir öğe ekleyip çıkarmanıza, bir setin bir öğeyi içerip içermediğini kontrol etmenize ve bir setin içeriğini çıktısını almanıza olanak tanıyan temel set işlevselliğini içerir.
local Set = {}
Set.__index = Set
-- Opsiyonel bir öğe listesi ile bir set oluşturma fonksiyonu
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Bir öğeyi bir sete eklemek için fonksiyon
function Set:add(item)
self[item] = true
end
-- Bir öğeyi bir setten çıkarmak için fonksiyon
function Set:remove(item)
self[item] = nil
end
-- Bir setin bir öğeyi içerip içermediğini kontrol etmek için fonksiyon
function Set:contains(item)
return self[item] == true
end
-- Hata ayıklama için seti virgülle ayrılmış bir liste olarak çıktısını almak için fonksiyon
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
endSet Oluşturma
Yeni bir set, opsiyonel bir öğe dizisi ile Set.new() çağrılarak oluşturulabilir.
local fruits = Set.new({"Elma", "Limon", "Portakal", "Kiraz", "Lime", "Şeftali"})Setin tanımına göre, bir setin sıralama kavramı yoktur.
Öğeyi Ekleme
Mevcut bir sete bir öğe eklemek, Set:add() metodu aracılığıyla yapılabilir.
local fruits = Set.new({"Elma", "Limon", "Portakal", "Kiraz", "Lime", "Şeftali"})
fruits:add("Mango")Öğeyi Çıkarma
Bir setten bir öğeyi çıkarmak için, öğe adını kullanarak Set:remove() çağırın.
local fruits = Set.new({"Elma", "Limon", "Portakal", "Kiraz", "Lime", "Şeftali"})
fruits:remove("Portakal")Öğeyi Kontrol Etme
Bir setin belirli bir öğeyi içerip içermediğini kontrol etmek için Set:contains() kullanın.
local fruits = Set.new({"Elma", "Limon", "Portakal", "Kiraz", "Lime", "Şeftali"})
local result1 = fruits:contains("Kiraz")
print(result1) -- true
local result2 = fruits:contains("Karpuz")
print(result2) -- falseEk Metotlar
Setler için diğer yararlı işlemler, setler arasında öğeleri karşılaştırmanıza, setleri birleştirmenize veya bir setten diğer bir seti çıkarmanıza olanak tanır.
Kesişim
Setleri Venn diyagramları olarak düşündüğünüzde, iki setin kesişimini aşağıdaki gibi alabilirsiniz, yani her iki set içinde bulunan öğeleri.
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", "Limon", "Portakal", "Kiraz", "Lime", "Şeftali"})
local frozenFruits = Set.new({"Mango", "Şeftali", "Ananas"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, ŞeftaliBirleşim
İki setin birleşimini aşağıdaki fonksiyon ile alabilirsiniz, yani her iki setin öğelerinin tekrarsız bir koleksiyonu. Bu fonksiyon, set1 + set2 toplama kısayolu sağlamak için metatable __add metodunu kullanır.
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({"Elma", "Mango", "Kiraz", "Şeftali"})
local sourFruits = Set.new({"Limon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- Şeftali, Lime, Elma, Kiraz, Limon, MangoÇıkarma
Bir setten diğer bir setin tüm öğelerini çıkarmak için aşağıdaki fonksiyonu kullanabilirsiniz. Yukarıdaki fonksiyona benzer şekilde, bu metatable __sub metodunu kullanarak set1 - set2 çıkarma kısayolu sağlar.
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({"Elma", "Limon", "Mango", "Kiraz", "Lime", "Şeftali"})
local sourFruits = Set.new({"Limon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Elma, Kiraz, Şeftali