(transfer) lua metatable

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.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326122334&siteId=291194637