Metatables, tabloların daha önce olduğundan daha güçlü hale gelmesini sağlar. Onlara veri bağlanır ve metametodu adı verilen değerleri içerir. Metametodlar, bağlandığı veritümü içeren belirli bir eylem kullanıldığında başlatılır.
Aşağıdaki kodu dikkate alın:
local list = {1, 2}print(list[3])
Bu kod listedeki üçüncü indeks aramak için listede aramak, hiçbir şey bulmak ve sonra nil'i iade etmek. Bu doğru değil, ancak. Aslında kod üçüncü indeks aramak için listede arar ve hiçbir şey bulmaz ve sonra metabelleği table'a bağlı olup olmadığını kontrol eder, bir metabelleği table’a bağlı olmadığınızda nil’i iade eder.
Metatables'i Manipülasyon
Bir tablenin mettablolarını eklemek ve bulmak için kullanılan iki ana işlev, şunlardır: setmetatable ve getmetatable
local x = {}local metaTable = {} -- metaTables da tablolar!setmetatable(x, metaTable) -- MetaTable adında bir metaTablosu verin!print(getmetatable(x)) --> table: [hexadecimal memory address]
setmetatable işlevi ayrıca mettablolarınızı ayarladığınız tablosunu da döndürür, bu yüzden bu iki script aynı şeyi yapar:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metodlar
Metametodlar, bir metabelleğin içinde saklanan işlevlerdir. Bir tablosunu çağırmak, bir tabloyu eklemek ve hatta bile bölmek dahil olmak üzere. Burada mevcut metametodların listesi:
Yöntem | Açıklama |
---|---|
__index(tablo, indeks) | tablasonu indekslenince ateşlenir, eğer tablsonu boşsa. Ayrıca, tablsonu indekslemek için de ayarlanabilir, bu durumda o tablsonun indekslenmesi olur. |
__newindex(tablo, indeks, değer) | tabla[index] = value olduğunda deneyin yapılmasına izin verir (tabla[index] = value), eğer table[index] boşsa. Ayrıca, tabla[index] = value'i yapılandırabilirsiniz, bu takdirde o tabla dizi olarak indekslenir. |
__call(tablo, ...) | Tablo bir işlev gibi çağrıldığında ateşlenir, ... geçen argelerdir. |
__concat(tablo, değer) | Masada .. katlanma operatörü kullanıldığında ateş eder. |
__unm(masa) | Tabelada tek - operatörü kullanıldığında ateşlenir. |
__add(tablo, değer) | Ekstra operatörü +. |
__sub(tablo, değer) | The - subtraction operatoru. |
__mul(tablo, değer) | The * mulitplication operator. |
__div(tablo, değer) | /division operatörü. |
__idiv(tablo, değer) | The // floor division operatörü. |
__mod(tablo, değer) | % modülü operatörü. |
__pow(tablo, değer) | ^ katsayı çarpanı operatörü. |
__tostring(masa) | FireWhenStringCallsTable'de çağrıldığında. |
__metable | Mevcutsa, mettabloları kilitler, böylece getmettabl will return this instead of the mettabl ve setmettabl will error. Non-Function value. |
__eq(tablo, değer) | The == operatörüne eşit |
__lt(tablo, değer) | The < operatöründen daha az |
__le(tablo, değer) | The << operator¹ |
__mod | Zayıf tablarda, bir tablinin anahtarlarının/veya değerlerinin zayıf olduğunu belirtir. Not: Roblox instanslarına ilişkileri olan tablarda asla geçersiz toplanmaz. Bunun gibi ilişkileri olan tablarda, geçersiz toplanan hiçbir geçersiz toplanma toplanmaz. |
__gc(masa) | Masa toplandığında ateşlenir. Not: Roblox'ta bu metod metodu devre dışı bırakıldı. |
__len(masa) | Nesnede # uzunluğu operatörü kullanıldığında yanıyor. |
__iter(masa) | Genelleştirilmiş itmeyi kullanırken özel bir iticiyi ifade etmek için kullanılır. |
Metodları aritmetik veya ilişkisel metodlar için yazarken iki işlev parametresi arasında değiştirilebilir olmalıdır. Örneğin, metodları ve metodların diğer değerleri arasındaki ilişkiyi gösteren tabloyu, metodları yazarken dikkatli olmalısınız. Örneğin, metodları ve metodların diğer değer
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a bir özelliktir ve b bir vektördür
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a bir vektördür, b bir skaldır
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örlerdir
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) -- 0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5
Metatables'ı Kullanıyor
Metatables'ı kullanmanın birçok yolu vardır, örneğin __unm metametodu (bir tablosunu olumsuz yapmak için):
local metatable = {
__unm = function(t) -- __unm tek - operatörü için
local negated = {}
for key, value in t do
negated[key] = -value -- bu masadaki tüm değerleri yok et
end
return negated -- tablosunu iade et
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Burada __index kullanarak şeyleri ilan etmenin ilginç bir yolunu var:
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index x'in tabloda indekslenmediği ve bulunmadığında __index tablosunda arandı ve birinde bulundu. Lua ardından __index tablosunda bir indeks bulundu ve bir tane döndürdü.
Artık bunu basit bir işlevle kolayca yapabilirsiniz, ancak bunun nereden geldiği konusunda çok daha fazlası var. Şu an örneğinizi alalım:
local t = {10, 20, 30}print(t(5))
Artık, bir tablosunu çağıramazsınız. Bu sadece çılgınca, ama (şaşır, şaşır!) metatabelerle yapabilirsiniz.
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Değere (5) argumentını ekleyin ve yeni tablo (t) içine yerleştirin.
end
return unpack(sum) -- Ayrı tablo değerlerini iade edin
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Ayrıca tablolar eklemek gibi çok daha fazlasını da yapabilirsiniz!
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Bu, bir tabloda aritmetik yapmaya çalıştığınızı söyleyerek hata verecek. Bunu metitable ile deneyelim.
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
Kullanım Düzenleyicilerini Kullan
Şimdi, tüm bu örnekler basit bir işlev olarak uygulanabilir, ancak bunun ötesine geçebilirsiniz. Belki gecikmiş bir matematik problemi içine koyulan bir basit program deneyelim.
Bunun için sadece __index metodunu kullanacağız:
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]) -- Bu sayıyı ilk kez kullandığından yavaş olacak, bu yüzden matematik işlevini çalıştırmak zorunda kalacak.
print(t[2]) -- bu sayıyı kullanıyor, bu yüzden yavaş olacaktır.
print(t[1]) -- will be fast because it's just grabbing the number from the table.
Rawset, Rawget, Rawequal
Metatables ile oynarken bazı sorunlar ile karşılaşabilirsiniz. Table'deki yeni değerleri oluşturmak için __index metametodunu kullanmanız gerekirse, ancak bu tablinin metatablosunda da bir __newindex metametodu varsa?
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- sadece bir örnek olarak
return self[i]
end,
__newindex = function(self, i, v)
--hiçbir şey yapmayın, çünkü normal yoluyla değerleri tabloya ayarlamanızı istemiyoruz
end
})
print(t[1]) -- Causes a C-Stack overflow
Şimdi neden bir Stack Overflow oluşturur? Stack Overflows, çok fazla kez bir işlevi kendi içinden çağırdığınızda gerçekleşir, ancak bunun neden olmasını sağlayacak şey self[i] değerine ayarlanır, yani
Problem şu ki, __newindex , değerleri belirlememizi sağlamıyor. Onun varlığı, standart t[i] = v yöntemiyle tablaya eklenen değerleri durdurur. Bu nedenle, bunun üstesinden gelmek için rawset işlevini kullanıyorsunuz.
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
--hiçbir şey yapmayın, çünkü normal yoluyla değerleri tabloya ayarlamanızı istemiyoruz
end
})
print(t[1]) -- prints 10
Set Veri Türü Kullanımı
Bir set , sıralanmış olmayan ve tekrar edilmeyen öğelerle dolu bir koleksiyondur. Bir öğe ya öğedir ya da ayarlaiçinde bulunmaktadır. Metatabeleri kullanarak, Lua kodları içinde setleri inşa edip manipüle edebilirsiniz.
Temel Yöntemler
Aşağıdaki kod temel set işlevlerini içerir, yeni setler oluşturmanızı, bir öğeyi eklemek ve kaldırmak ve bir ayarlaiçeriğini görmek için aşağıdaki kodu içerir.
local Set = {}
Set.__index = Set
-- Bir seti zorunlu bir listeden oluşturmak için işlev
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 ayarlaeklemek için işlev
function Set:add(item)
self[item] = true
end
-- Bir ayarlabir öğeyi kaldırmak için işlev
function Set:remove(item)
self[item] = nil
end
-- Bir setin bir öğesi olup olmadığını kontrol etmek için işlev
function Set:contains(item)
return self[item] == true
end
-- Hata ayıklama için bir koma ayrılımlı liste olarak çıktı içerik
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Set Oluştur
Yeni bir set oluşturmak için şu adımları izleyin: Set.new() ile bir seçenekli bir dizi öğe ekleyin.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Ayrıca, tanım olarak, bir setin herhangi bir siparişi olmaz.
Eşya Ekle
Mevcut bir sete bir öğe eklemek Set:add() yöntemi kullanılarak yapılabilir.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Eşyayı Kaldır
Bir ayarlabir öğeyi kaldırmak için, öğenin adıyla birlikte Set:remove() çağır.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Eşyaya Göre Ara
Bir setin belirli bir öğeyi içerdiğini kontrol etmek için Set:contains() kullanın.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- doğrulocal result2 = fruits:contains("Watermelon")print(result2) -- false
Ekstra Yöntemler
Setler için diğer yararlı işlemler uygulanabilir, böylece öğeleri bir setten diğerine karşılaştırabilirsiniz, setleri birleştirebilir veya bir seti diğerinden bir setten çıkarabilirsiniz.
Kavşak
Setleri Venn grafikleri olarak düşündüğünüzde, iki setin çakışma noktasını şu şekilde elde edebilirsiniz, yani görünen öğelerin her iki sette de göründüğünü.
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
Birleştirme
Aşağıdaki işlevi kullanarak iki setin birleşimini alabilirsiniz, yani her iki setin de duplik olmayan bir koleksiyonu ifade eden bir koleksiyon. Bu işlev, metabelleği __add yöntemini kullanarak ek bir kısayol sağlar __add .
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
Tersyüz
Aşağıdaki işlev aracılığıyla diğer setteki tüm öğeleri kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağından kaynağı
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