Metadata table and metadata method in Lua

Source: Internet
Author: User
Preface


Each value in Lua can have a metadata table. A metabase is a normal Lua table that defines the behavior of the original values in certain operations. You can set a specific field in the original table of a value to change certain behavior characteristics of operations acting on the value.

For example, when the numeric value is used as the operand of addition, Lua checks whether the "_ Add" field in its meta table has a function. If yes, Lua calls it to execute addition.

We call the key in the meta table an event and the value metamethod ). In the preceding example, the event is "add", and the metamethod is the addition function.

You can use the getretriable function to query metadata tables of any value.

In table, the meta methods I can redefine include the following:

_ Add (a, B) -- addition _ Sub (a, B) -- subtraction _ MUL (a, B) -- multiplication _ Div (A, B) -- Division _ Mod (a, B) -- modulo _ POW (a, B) -- multiplication power _ UNM (a) -- inverse number _ Concat (A, B) -- connection _ Len (a) -- length _ eq (a, B) -- equal _ lt (a, B) -- less than _ LE (A, B) -- less than or equal to _ index (a, B) -- index query _ newindex (a, B, c) -- Index Update (PS: I will talk about it later) _ call (,...) -- execution method call _ tostring (a) -- string output _ resumable -- protects the metadata table

Every table in Lua has its retriable. By default, Lua creates a new table without retriable.

t = {}print(getmetatable(t)) --> nil
You can use the setretriable function to set or change the retriable of a table.
t1 = {}setmetatable(t, t1)assert(getmetatable(t) == t1)

Any table can be the retriable of another table, and a group of related tables can share a retriable (describing their common behavior ). A table can also be its own retriable (describing its private behavior ).

Next we will introduce how to redefine these methods.


Meta-Methods of arithmetic classes


Now I use the complete instance code to describe in detail how to use the arithmetic metacharacter method.

Set = {} Local Mt = {} -- meta table of the set -- creates a function set based on the values in the parameter list. new (l) Local set ={} setmetatable (set, MT) for _, V in pairs (l) do set [v] = true end return setend -- Union set operation function set. union (a, B) Local retset = set. new {} -- this is equivalent to set. new ({}) for V in pairs (a) Do retset [v] = true end for V in pairs (B) do retset [v] = true end return retsetend -- intersection operation function set. intersection (a, B) Local retset = set. new {} for V in pairs (a) Do retset [v] = B [v] end return retsetend -- print the operation function set of the set. tostring (SET) Local TB ={} for e in pairs (SET) Do TB [# TB + 1] = e end return "{".. table. concat (TB ,",").. "}" end function set. print (s) print (set. tostring (s) End
Now, I define "+" to calculate the union of the two sets, so we need to share a meta table with all the tables used to represent the set, in addition, the meta table defines how to perform an addition operation. First, create a regular table, prepare the meta table used as the set, and then modify the set. New function. Each time you create a set, a meta table is set for the new set. The Code is as follows:

Set = {} Local Mt = {} -- meta table of the set -- creates a function set based on the values in the parameter list. new (l) Local set ={} setretriable (set, MT) for _, V in pairs (l) do set [v] = true end return setend
After that, all the sets created by set. New have the same meta table, for example:

local set1 = Set.new({10, 20, 30})local set2 = Set.new({1, 2})print(getmetatable(set1))print(getmetatable(set2))assert(getmetatable(set1) == getmetatable(set2))
Finally, we need to add the metadata Method to the metadata table. The Code is as follows:

mt.__add = Set.union
After this, as long as we use the "+" symbol to calculate the union of the two sets, it will automatically call the set. Union function and pass the two operands as parameters. For example, the following code:

local set1 = Set.new({10, 20, 30})local set2 = Set.new({1, 2})local set3 = set1 + set2Set.print(set3)
All the element methods that can be redefined listed above can be redefined using the above method. Now there is a new problem. set1 and set2 both have metabases. Who should we use? Although the sample code here uses a meta table, in actual coding, we will encounter the problem I mentioned here. For this problem, Lua solves the problem by following the steps below:
  1. For binary operators, if the first operand has a metadatabase table and a field definition is required in the metadatabase table, for example, the _ add metadatabase method is defined here, then Lua uses this field as the metadata method, but it has nothing to do with the second value;
  2. For binary operators, if the first operand has a meta table, but the meta table does not have the required field definition, for example, the _ add meta method definition here, then Lua searches for the meta table of the second operand;
  3. If neither of the two operands has a metadata table or a corresponding metadata method definition, Lua raises an error.
The above are the rules for Lua to handle this problem. How should we do it in actual programming?

For example, if set3 = set1 + 8, the following error message is printed:

lua: test.lua:16: bad argument #1 to ‘pairs‘ (table expected, got number)
However, in actual encoding, We can pop up the defined error message as follows:

Function set. Union (a, B) If getretriable ()~ = Mt or getretriable (B )~ = MT then error ("retriable error. ") end local retset = set. new {} -- this is equivalent to set. new ({}) for V in pairs (a) Do retset [v] = true end for V in pairs (B) Do retset [v] = true end return retsetend
When the two operands of the metadatabase table are not the same metadatabase table, the problem occurs when the two are in the Union, so we can print the expected error message.

The above summarizes the definition of the meta-Methods of the arithmetic class. The meta-Methods of the relational class and the meta-Methods of the arithmetic class are similar.


_ Tostring metadata Method


Anyone who has written Java or C # knows that there is a tostring method in the object class. programmers can rewrite this method to meet their own needs. In Lua, this is also true. When we print (A) (a is a table) directly, it is not possible. In this case, we need to re-define the _ tostring meta method so that print can format and print table data.
The function print always calls tostring to format the output. When formatting any value, tostring checks whether the value has a _ tostring metadata method. If so, tostring uses this value as a parameter to call this metadata method. The actual formatting operation is completed by the function referenced by the _ tostring metadata method, this function returns a formatted string. For example, the following code:

mt.__tostring = Set.toString


How to protect our "Cheese"-metabases


We will find that using getmetatable can easily obtain the metadata table, and using setmetatable can easily modify the metadata table. Is this too risky, so how can we protect our meta tables from tampering? In Lua, The setretriable and getretriable functions use a field in the meta table to protect the meta table. This field is _ retriable. To protect the meta-table of a set, you must use the _ retriable field to neither view nor modify the meta-table of the set. When this field is set, getretriable will return the value of this field, and setretriable will cause an error. See the following DEMO code:

Function set. new (l) Local set ={} setretriable (set, MT) for _, V in pairs (l) do set [v] = true end Mt. _ resumable = "you cannot get the resumable" -- after setting my meta table, do not allow others to set return setend local TB = set. new ({1, 2}) print (TB) print (getretriable (TB) setretriable (TB ,{})
The above code prints the following content:

{1, 2}You cannot get the metatablelua: test.lua:56: cannot change a protected metatable


_ Index meta Method


Do you still remember what value will be returned when we access a field that does not exist in the table? By default, when we access a field that does not exist in a table, the result is nil. However, this situation is easily changed; Lua determines whether to return nil or other values according to the following steps:

  1. When accessing a table field, if the table has this field, the corresponding value is directly returned;
  2. If the table does not have this field, the interpreter is prompted to search for a metadata method named _ index. Next, the interpreter calls the corresponding metadata method and returns the value returned by the metadata method;
  3. If the meta method is not available, the nil result is returned.


The following uses an actual example to describe the use of _ index. Assume that you want to create some description windows. Each table must describe some window parameters, such as color, position, and size. These parameters have default values. Therefore, when creating window objects, we can specify parameters different from the default values.

Windows ={} -- create a namespace -- create a default table windows. default = {x = 0, y = 0, width = 100, Height = 100, color = {r = 255, G = 255, B = 255} windows. mt ={} -- create a metadatabase -- declare the constructor function for Windows. new (o) setretriable (O, windows. MT) return oend -- defines the _ index metamethod for Windows. mt. _ Index = function (table, key) return windows. default [Key] end local win = windows. new ({x = 10, y = 10}) print (win. x) --> 10 access the value of print (win. width) --> 100 access the value print (win. color. r) --> 255 access the value in the default table

Based on the output of the above Code and the three steps mentioned above, let's take a look at print (win. x), since the win variable itself has the X field, the value of its own field is printed directly; print (win. width). Because the win variable itself does not have the width field, you can check whether the table has a meta-table and whether the meta-method corresponding to _ index exists in the meta-table. Because of the existence of the _ index meta-method, returns the value of the width field in the default table, print (win. color. r.

In actual programming, the __index metamethod does not have to be a function and can also be a table. When it is a function, Lua calls this function using table and non-existent key as parameters, which is the same as the above Code; when it is a table, lua re-accesses the table in the same way, so the above Code can also be like this:

-- Define the _ index metamethod windows. mt. _ Index = windows. Default

_ Newindex meta Method


The _ newindex meta method is similar to _ index, __newindex is used to update data in the table, while _ index is used to query data in the table. When assigning values to indexes that do not exist in a table, perform the following steps in Lua:

The Lua interpreter first checks whether the table has a metadata table;


  1. If a metadatabase table is available, check whether the metadatabase table has the _ newindex metadatabase method. If no metadatabase table exists, add the index directly and assign values accordingly;
  2. If the _ newindex meta method exists, the Lua interpreter executes it instead of assigning values;
  3. If the _ newindex parameter is not a function but a table, the Lua interpreter will assign values to the table instead of the original table.

The following code shows a problem:

local tb1 = {}local tb2 = {} tb1.__newindex = tb2tb2.__newindex = tb1 setmetatable(tb1, tb2)setmetatable(tb2, tb1) tb1.x = 10

Have you found any problems? Is it a loop? In the Lua interpreter, an error message will pop up for this problem. The error message is as follows:

loop in settable

Reference blog: http://www.jellythink.com/archives/511

Metadata table and metadata method in Lua

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.