Examples are given to illustrate the use of meta tables and meta methods in Lua

Time:2021-11-27

The meta table of table provides a mechanism to redefine some operations of table.
After that, we will see how the meta table supports the prototype behavior similar to JS.

 

Copy codeThe code is as follows:
f1 = {a = 1, b = 2}   — Represents a fraction A / b
f2 = {a = 2, b = 3}

 

 

Copy codeThe code is as follows:

–This is wrong:
— s = f1 + f2

 

metafraction = {}
function metafraction.__add(f1, f2)
  sum = {}
  sum.b = f1.b * f2.b
  sum.a = f1.a * f2.b + f2.a * f1.b
  return sum
end

setmetatable(f1, metafraction)
setmetatable(f2, metafraction)

s = f1 + f2   — Call on the meta table of F1__ Add (F1, F2) method

–F1 and F2 do not have keys that can access their meta tables, which is different from prototype,
–So you must use getmetatable (F1) to get the meta table. A meta table is an ordinary table,
–Lua can access its key in the usual way, such as__ add。

 

 

Copy codeThe code is as follows:

–However, the following code is wrong because s has no meta table:
— t = s + s
–The following patterns in the form of classes can solve this problem:

 

Meta table__ Index can overload the lookup of point operators:
defaultFavs = {animal = ‘gru’, food = ‘donuts’}
myFavs = {food = ‘pizza’}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal   — Can work! Thanks for the support of meta table

 

If you fail to directly find the key in the table, the meta table will be used__ Index continues to search, and is a recursive search

__ The value of index can also be function (TBL, key), which can support more customized searches.

 __ index、__ Add and so on are called meta methods.
Here is a complete list of metamethods of table:

— __add(a, b)                     for a + b
— __sub(a, b)                     for a – b
— __mul(a, b)                     for a * b
— __div(a, b)                     for a / b
— __mod(a, b)                     for a % b
— __pow(a, b)                     for a ^ b
— __unm(a)                        for -a
— __concat(a, b)                  for a .. b
— __len(a)                        for #a
— __eq(a, b)                      for a == b
— __lt(a, b)                      for a < b
— __le(a, b)                      for a <= b
— __index(a, b)  <fn or a table>  for a.b
— __newindex(a, b, c)             for a.b = c
— __call(a, …)                  for a(…)

Class style table and inheritance


Classes are not built-in; There are different methods to implement through tables and meta tables.

The following is an example, followed by an explanation of the example

 

Copy codeThe code is as follows:

Dog = {}                                   — 1.

 

function Dog:new()                         — 2.
  newObj = {sound = ‘woof’}                — 3.
  self.__index = self                      — 4.
  return setmetatable(newObj, self)        — 5.
end

function Dog:makeSound()                   — 6.
  print(‘I say ‘ .. self.sound)
end

mrDog = Dog:new()                          — 7.
mrDog:makeSound()  — ‘I say woof’         — 8.

–1. Dog looks like a class; In fact, it is completely a table.
–2. The function tablename: FN (…) is the same as the function tablename.fn (self,…)
—     The colon (:) just adds self as the first argument.
—     Articles 7 and 8 below illustrate how the self variable gets its value.
–3. Newobj is an instance of the dog class.
–4. Self is the initialized class instance. Usually self = dog, but inheritance can change this.
—     If the meta table of newobj and__ Index is set to self,
—     Newobj can get the function of self.
–5. Remember: setmetatable returns its first parameter.
–6. The colon (:) works in Article 2, but here we expect
—     Self is an instance, not a class
–7. Similar to dog. New (dog), so self = dog in new().
–8. Same as mrdog. Makesound (mrdog); self = mrDog。

 

Examples of inheritance:

 

Copy codeThe code is as follows:

LoudDog = Dog:new()                           — 1.

 

function LoudDog:makeSound()
  s = self.sound .. ‘ ‘                       — 2.
  print(s .. s .. s)
end

seymour = LoudDog:new()                       — 3.
seymour:makeSound()  — ‘woof woof woof’      — 4.

–1. Louddog obtains the list of methods and variables of dog.
–2. Through new(), self has a ‘sound’ key from new(). See article 3.
–3. It is the same as louddog. New (louddog) and is converted to
—     Dog. New (louddog), because louddog has no ‘new’ key,
—     But you can see it in its meta table__ index = Dog。
—     Result: the meta table of Seymour is louddog, and
—     LoudDog.__ index = LoudDog。 So there is seymour.key
—     = Seymour.key, louddog.key, dog.key, see
—     Which table comes first for a given key.
–4. You can find the key of ‘makesound’ in louddog; This and
—     Louddog. Makesound (Seymour).

 

 

Copy codeThe code is as follows:
–If necessary, the subclass can also have new (), which is similar to that of the base class:
function LoudDog:new()
  newObj = {}
  — Initialize newobj
  self.__index = self
  return setmetatable(newObj, self)
end