Dantez
Moim sekretem jest ciasto
- Joined
- May 22, 2008
- Messages
- 1,206
- Reaction score
- 202
Autor: Dantez
Metamethod to funkcja, kt?ra zostaje wywo?ana, kiedy okre?lona operacja zostaje wykonana na tabeli. Ich dodatkow? specjaln? w?a?ciwo?ci? jest to, ?e wszystkie zaczynaj? si? od dw?ch znak?w "_". Istnieje kilka metamethod, kt?re mo?emy wykorzysta? do modyfikowania dzia?ania tabel, ich dzia?anie mo?emy znale?? np. .
[LUA]Position = {}
PositionMT = {}
setmetatable(Position, PositionMT)[/LUA]
Mo?emy r?wnie? uzyska? dost?p do metatable danej tabeli poprzez funkcj? getmetatable.
[LUA]Position = {}
PositionMT = {}
setmetatable(Position, PositionMT)
local mt = getmetatable(Position)
-- mt zawiera teraz tabel? PositionMT[/LUA]
Napiszmy teraz przyk?adowy kod, w kt?rym wykorzystamy metametody __index i __newindex. Pierwszy z nich jest wykonywany, gdy pr?bujemy odwo?a? si? do elementu tabeli, drugi za? podczas, gdy pr?bujemy nowy element stworzy?.
[LUA]Position = {}
PositionMT = {}
PositionMT.__newindex = function(self, index, value)
print("__newindex:", index, value)
end
PositionMT.__index = function(self, index)
print("__index:", index)
return true
end
setmetatable(Position, PositionMT)
Position.x = 10
local a = Position.x
local b = Position[4]
local c = Position["Dantez"]
print(type(a))[/LUA]
Dziej? si? dwie ciekawe rzeczy:
1) Position.x = 10 nie zmienia warto?ci w tabeli
2) Position.x, Position[4] i Position["Dantez"] zwracaj? true
Problem z __index i __newindex polega na tym, ?e gdyby?my chcieli zwr?ci? lub zmieni? realne warto?ci dla tabeli poprzez "." (kropk?) lub "[]" (nawiasy kwadratowe) to spowodujemy zap?tlenie owych funkcji. Z pomoc? przychodz? funkcje rawset i rawget, kt?re niejako omijaj? ponowne wykonywanie __index i __newindex.
[LUA]Position = {}
PositionMT = {}
PositionMT.__newindex = function(self, index, value)
rawset(self, index, value)
end
PositionMT.__index = function(self, index)
return rawget(self, index)
end
setmetatable(Position, PositionMT)
Position.x = 10
local a = Position.x
print(a) -- -> 10[/LUA]
[LUA]Position = {x = 0, y = 0, z = 0, stackpos = 0}
PositionMT = {}
PositionMT.__tostring = function(self)
return "{x= " .. self.x .. ", y= " .. self.y .. ", z= " .. self.z .. ", stackpos= " .. self.stackpos .. "}"
end
PositionMT.__concat = function(self, value)
if(self.stackpos ~= nil) then
return tostring(self) .. value
else
return self .. tostring(value)
end
end
setmetatable(Position, PositionMT)
print(Position .. " to moja pozycja")
print("Moja pozycja to: " .. Position)
print("Pozycja: " .. tostring(Position))[/LUA]
Sama funkcja __tostring powinna by? zrozumia?a, jednak w __concat dzieje si? troszk? wi?cej. Nie jeste?my w stanie okre?li? jakiego dok?adnie 'typu' b?dzie dany parametr, zawsze zwr?cone zostan? nam typy podstawowe jak np. string, czy table. Funkcja __concat zak?ada, ?e pierwszym parametrem b?dzie tabela, do kt?rej jest przypisana. Aby ??czenie string?w dzia?a?o w obie strony, mo?na sprawdza?, czy dany parametr to tabela lub czy posiada kt?ry?/wszystkie z element?w Position. Nie jest to dok?adne, w pewnych przypadkach nie zadzia?a, ale zawsze mo?na u?ywa? tostring().
[LUA]Position = {}
Position.__index = Position;
function Position.new(x, y, z, stackpos)
return setmetatable({x = x or 0, y = y or 0, z = z or 0, stackpos = stackpos or 0}, Position)
end
function Position.__add(self, other)
if(type(other) == "table") then
return Position.new(self.x + (other.x or 0), self.y + (other.y or 0), self.z + (other.z or 0), self.stackpos)
else
return self
end
end
function Position.__sub(self, other)
if(type(other) == "table") then
return Position.new(self.x - (other.x or 0), self.y - (other.y or 0), self.z - (other.z or 0), self.stackpos)
else
return self
end
end
function Position.__tostring(self)
return "{x=" .. self.x .. ", y=" .. self.y .. ", z=" .. self.z .. ", stackpos=" .. self.stackpos .. "}"
end
function Position.__concat(self, value)
if(self.stackpos ~= nil) then
return tostring(self) .. value
else
return self .. tostring(value)
end
end
setmetatable(Position, { __call = function(_, ...) return Position.new(...) end })
-- Testing
local pos1 = Position(30, 50, 60, 1)
local pos2 = Position.new(90, 10, 80, 55)
print(pos1, pos2)
pos1 = pos1 + {x=40, y=90}
pos2 = pos2 - {x=1, z=2}
print(pos1, pos2)[/LUA]
Rozdzia? 1 - Metamethods
Metamethod to funkcja, kt?ra zostaje wywo?ana, kiedy okre?lona operacja zostaje wykonana na tabeli. Ich dodatkow? specjaln? w?a?ciwo?ci? jest to, ?e wszystkie zaczynaj? si? od dw?ch znak?w "_". Istnieje kilka metamethod, kt?re mo?emy wykorzysta? do modyfikowania dzia?ania tabel, ich dzia?anie mo?emy znale?? np. .
- __index
- __newindex
- __add
- __sub
- __mul
- __div
- __mod
- __pow
- __unm
- __eq
- __lt
- __lt
- __le
- __le
- __call
- __concat
- __tostring
- __len
- __mode
- __metatable
- __gc
Rozdzia? 2 - Metatable
Metatable to nic innego, jak tabela, kt?ra zawiera metamethody. W takiej postaci mo?emy j? zaaplikowa? do tabeli poprzez funkcj? setmetatable.[LUA]Position = {}
PositionMT = {}
setmetatable(Position, PositionMT)[/LUA]
Mo?emy r?wnie? uzyska? dost?p do metatable danej tabeli poprzez funkcj? getmetatable.
[LUA]Position = {}
PositionMT = {}
setmetatable(Position, PositionMT)
local mt = getmetatable(Position)
-- mt zawiera teraz tabel? PositionMT[/LUA]
Rozdzia? 3 - __index i __newindex
Napiszmy teraz przyk?adowy kod, w kt?rym wykorzystamy metametody __index i __newindex. Pierwszy z nich jest wykonywany, gdy pr?bujemy odwo?a? si? do elementu tabeli, drugi za? podczas, gdy pr?bujemy nowy element stworzy?.
[LUA]Position = {}
PositionMT = {}
PositionMT.__newindex = function(self, index, value)
print("__newindex:", index, value)
end
PositionMT.__index = function(self, index)
print("__index:", index)
return true
end
setmetatable(Position, PositionMT)
Position.x = 10
local a = Position.x
local b = Position[4]
local c = Position["Dantez"]
print(type(a))[/LUA]
Code:
__newindex: x 10
__index: x
__index: 4
__index: Dantez
boolean
Dziej? si? dwie ciekawe rzeczy:
1) Position.x = 10 nie zmienia warto?ci w tabeli
2) Position.x, Position[4] i Position["Dantez"] zwracaj? true
Problem z __index i __newindex polega na tym, ?e gdyby?my chcieli zwr?ci? lub zmieni? realne warto?ci dla tabeli poprzez "." (kropk?) lub "[]" (nawiasy kwadratowe) to spowodujemy zap?tlenie owych funkcji. Z pomoc? przychodz? funkcje rawset i rawget, kt?re niejako omijaj? ponowne wykonywanie __index i __newindex.
[LUA]Position = {}
PositionMT = {}
PositionMT.__newindex = function(self, index, value)
rawset(self, index, value)
end
PositionMT.__index = function(self, index)
return rawget(self, index)
end
setmetatable(Position, PositionMT)
Position.x = 10
local a = Position.x
print(a) -- -> 10[/LUA]
Rozdzia? 4 - __tostring i __concat
Metametoda __tostring wykonywana jest, gdy u?ywamy funkcji tostring(), natomiast __concat wykonywane jest podczas pr?by przy??czenia tabeli do stringu poprzez "..". Przejd?my od razu do przyk?adu.[LUA]Position = {x = 0, y = 0, z = 0, stackpos = 0}
PositionMT = {}
PositionMT.__tostring = function(self)
return "{x= " .. self.x .. ", y= " .. self.y .. ", z= " .. self.z .. ", stackpos= " .. self.stackpos .. "}"
end
PositionMT.__concat = function(self, value)
if(self.stackpos ~= nil) then
return tostring(self) .. value
else
return self .. tostring(value)
end
end
setmetatable(Position, PositionMT)
print(Position .. " to moja pozycja")
print("Moja pozycja to: " .. Position)
print("Pozycja: " .. tostring(Position))[/LUA]
Code:
{x= 0, y= 0, z= 0, stackpos= 1} to moja pozycja
Moja pozycja to: {x= 0, y= 0, z= 0, stackpos= 1}
Pozycja: {x= 0, y= 0, z= 0, stackpos= 1}
Sama funkcja __tostring powinna by? zrozumia?a, jednak w __concat dzieje si? troszk? wi?cej. Nie jeste?my w stanie okre?li? jakiego dok?adnie 'typu' b?dzie dany parametr, zawsze zwr?cone zostan? nam typy podstawowe jak np. string, czy table. Funkcja __concat zak?ada, ?e pierwszym parametrem b?dzie tabela, do kt?rej jest przypisana. Aby ??czenie string?w dzia?a?o w obie strony, mo?na sprawdza?, czy dany parametr to tabela lub czy posiada kt?ry?/wszystkie z element?w Position. Nie jest to dok?adne, w pewnych przypadkach nie zadzia?a, ale zawsze mo?na u?ywa? tostring().
Rozdzia? 5 - Podsumowanie
Metatables bardzo u?atwiaj? prace nad skryptami, poniewa? mo?na ich potem u?ywa? wielokrotnie. Oto prosty przyk?ad, jak mo?na je wykorzysta?:[LUA]Position = {}
Position.__index = Position;
function Position.new(x, y, z, stackpos)
return setmetatable({x = x or 0, y = y or 0, z = z or 0, stackpos = stackpos or 0}, Position)
end
function Position.__add(self, other)
if(type(other) == "table") then
return Position.new(self.x + (other.x or 0), self.y + (other.y or 0), self.z + (other.z or 0), self.stackpos)
else
return self
end
end
function Position.__sub(self, other)
if(type(other) == "table") then
return Position.new(self.x - (other.x or 0), self.y - (other.y or 0), self.z - (other.z or 0), self.stackpos)
else
return self
end
end
function Position.__tostring(self)
return "{x=" .. self.x .. ", y=" .. self.y .. ", z=" .. self.z .. ", stackpos=" .. self.stackpos .. "}"
end
function Position.__concat(self, value)
if(self.stackpos ~= nil) then
return tostring(self) .. value
else
return self .. tostring(value)
end
end
setmetatable(Position, { __call = function(_, ...) return Position.new(...) end })
-- Testing
local pos1 = Position(30, 50, 60, 1)
local pos2 = Position.new(90, 10, 80, 55)
print(pos1, pos2)
pos1 = pos1 + {x=40, y=90}
pos2 = pos2 - {x=1, z=2}
print(pos1, pos2)[/LUA]