This article is abbreviated from a foreigner's blog. It's well written. Unfortunately, my translation is too bad. The abbreviated translation is as follows.
(key--value is commonly translated as "key-value pair", I translate it as index, value)
In this tutorial, I will introduce an important concept in Lua: metatable (metatable), mastering the metatable can make you more effective The
use Lua. Each tabel can attach a metatable, which is a table with a collection of indexes that can change the behavior of the attached table.
Look at the following example:
t = {} -- normal table
mt = {} -- meta table, there is nothing for now
setmetatable(t, mt) -- meta table with mt set to t
getmetatable(t) -- this time Return mt
as you can see getmetatable and setmetatable are the main functions. Of course we could combine the three lines above:
t = setmetatable({}, {})
setmetatable returns the first argument, so we can use this short expression. Now, what do we put in a metatable
? can contain anything, but a metatable is usually
called with an index (of course string type) starting with "__" (two underscores), such as __index and __newindex . The value corresponding to the index can be a table or a function, for example:
t = setmetatable({}, {
__index = function(t, key)
if key == "foo" then
return 0
else
return table[key]
end
end
})
We assign a function to the __index index, let's see what this index does.
Probably the most commonly used index in the __index
metatable is __index, which can contain tables or functions.
When you access a table by index, no matter what it is (such as t[4], t.foo, and t["foo"]), and a value that doesn't have an index assigned,
Lua first looks for the existing index , then look for the __index index in the table's metatable (if it has one). If
__index contains a table, Lua will look for the index in the table contained in __index. This sounds confusing, let's look at an example.
other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo -- 3 , now __index contains table {foo=3} looking
for t.bar -- nil , not found
if _ _index contains a function that, when called, passes the table and index being accessed as arguments. From the above example,
we can use indexes with conditional statements, as well as arbitrary Lua statements. So in this case, if the index is
equal to the string "foo", we can return 0, otherwise, we can look up the index used in the table; when "foo" is used, let t be the value of the table
aliases and returns 0. (This sentence is not very clear, the original text is: Therefore, in that example, if the key was equal to
the string "foo" we would return 0, otherwise we look up the table table with the key that
was used; this makes t an alias of table that returns 0 when the key "foo" is used.)
You may be wondering how to pass the table as the first parameter to the __index function. This is convenient when you use the same metatable in multiple tables
, supports code reuse and saves computer resources. We'll see the explanation in the Vector class at the bottom.
--note : below is an example of mine
other = function(t,k) if k=="foo" then return 0 end end
t = setmetatable({}, { __index = other })
print(t.foo)
__newindex
Next is __newindex, which is similar to __index. Like __index, it can contain functions and tables. When you assign
a value to a , Lua will look for __newindex in the metatable, in the same order as __index. If __newindex is a table, the
index and value will be set to the specified table:
other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3 -- there is no foo in t, check __newindex, and pass foo=3 to other, and do not assign
other.foo to foo in t – 3 so it is 3
t.foo – nil so it is nil
as expected, when __newindex is a function, it will pass three parameters of table, index and value when it is called.
t = setmetatable({}, {
__newindex = function(t, key, value)
if type(value) == "number" then
rawset(t, key, value * value)
else
rawset(t, key, value)
end
end
})
t.foo = "foo"
t.bar = 4
t.la = 10
t.foo -- "foo"
t.bar -- 16
t.la -- 100
When creating a new index in t, if The value is number, the value will be squared, otherwise nothing will be done. The following introduces rawget and rawset.
rawget and rawset
Sometimes you need to get and set the index of a table and don't want to use metatable. As you might guess, rawget allows you to get the index without __index, and
rawset allows you to set the value of the index without __newindex (no, compared to the traditional metatable way, these do not accelerate). You only need to use them to avoid getting stuck
in infinite loops. In the above example, t[key] = value * value will call the __newindex
function again, which will put your code in an infinite loop. Use rawset(t, key, value * value) to avoid it.
As you may see, to use these functions, we have to pass the parameters target table, key, and value when you use rawset.
Operators
Many metatable indexes are operators (eg, +, -, etc.) that allow you to perform some operator operations using the table. For example, if we wanted a table to support the
multiplication operator (*), we could do:
t = setmetatable({ 1, 2, 3 }, {
__mul = function(t, other) ,
new = {}
for i = 1 , other do
for _, v in ipairs(t) do table.insert(new, v) end
end
return new
end
})
t = t * 2 -- { 1, 2, 3, 1, 2, 3 }
This allows us to create a new table that repeats certain times using the multiplication operator. You can also see that the index equivalent of __mul and multiplication is that,
unlike __index and __newindex, operator indexes can only be functions. The first argument they accept is always the target list, followed
by the rvalue (except for the unary operator "-", which is the index __unm). Here is the list of operators:
__add: addition (+)
__sub: subtraction (-)
__mul: multiplication (*)
__div: division (/)
__mod: modulo (%)
__unm: negation (-), unary operator
__concat: concatenation (..)
__eq: equal (==)
__lt: less than (<)
__le: less than or equal (<=)
(only ==, <, <= , because you can do everything above, in fact == and < is enough)
__call
is followed by the __call index, which allows you to call the table as a function, code example:
t = setmetatable({}, {
__call = function(t, a, b, c, whatever)
return ( a + b + c) * whatever
end
})
t(1, 2, 3, 4) –- 24 , the table t looks for __call when calling, and calls the function inside, t is equivalent to a function
Functions in call, as usual, are passed a target list, and some parameters. __call is very useful and is often used to forward calls between a table
and the functions in it (it's used for is forwarding a call on a table to a function inside
that table.). kikito's tween.lua library is an example where tween.start can be called by itself (tween). Another example is
MiddleClass, where the new function in a class can be called by the class itself.
__tostring
The last one is __tostring. If it is implemented, then tostring can convert the table to string, which is very convenient for
functions . Normally, when you convert table to string, you need "table: 0x<hex-code-here", but you can
solve it with __tostring only. Example:
t = setmetatable({ 1, 2, 3 }, {
__tostring = function(t)
sum = 0
for _, v in pairs(t) do sum = sum + v end
return "Sum: " .. sum
end
} )
print(t) -- prints out "Sum: 6"
creates a vector class
Now let's encapsulate a 2D vector class (thanks to hump.vector for a lot of code). The code is too long, you can check gist #1055480,
there are a lot of metatable concepts in the code, (note that it may be a bit difficult if you have no experience with object orientation before).
Vector = {}
Vector.__index = Vector
first declares a Vector class, setting the __index index to point to itself. What is this doing? You'll notice that we've put all
the metatables in the Vector class. You will see the easiest way to implement OOP (Object-Oriented Programming) in Lua. The Vector
table represents the class, it contains all the methods, the instance of the class can be created by Vector.new (below).
function Vector.new(x, y)
return setmetatable({ x = x or 0, y = y or 0 }, Vector)
end
It creates a new table with x, y attributes, then sets metatable to Vector kind. We know that Vector contains all
the metamethods, especially __index. This means that we can use all the methods in Vector through the new table.
Another important line is:
setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })
This means we can create a new Vector instance via Vector.new or just Vector.
Last but not least, you may not have noticed the colon syntax. When we define a function with a colon like this:
function t:method(a, b, c)
-- ...
end
What we really define is the function:
function t.method(self, a, b, c )
-- ...
end
This is a syntactic sugar that helps us use OOP. When calling a function, we can use the colon syntax like this:
-- these are the same
t:method(1, 2, 3)
t.method(t, 1, 2, 3)
How do we use the Vector class? Example:
a = Vector.new(10, 10)
b = Vector(20, 11)
c = a + b
print(a:len()) -- 14.142135623731
print(a) -- (10, 10)
print(c) -- (30, 21)
print(a < c) -- true
print(a == b) -- false
Since Vector has __index in it, we can use all its methods in the instance.
in conclusion
Thanks for reading, I hope you learned something. If you have suggestions or questions, please leave a comment in the comments section, I want to hear your reply!
Original: http://www.cnblogs.com/xdao/archive/2013 /04/02/lua-metatable.html
Author: Banshan
Source : http://www.cnblogs.com/xdao/
The copyright of this article belongs to the author and the blog garden. Welcome to reprint, but this statement must be retained without the author's consent. The link to the original text is given in an obvious position on the article page, otherwise, the right to pursue legal responsibility is reserved.
(transfer) lua metatable
Guess you like
Origin http://43.154.161.224:23101/article/api/json?id=326122334&siteId=291194637
Recommended
Ranking