3. Tables
1. Tables
----------------------------------------------------
-- 3. Tables.
----------------------------------------------------
-- Tables = Lua's only compound data structure;
-- they are associative arrays.
-- Similar to php arrays or js objects, they are
-- hash-lookup dicts that can also be used as lists.
-- Using tables as dictionaries / maps:
-- Dict literals have string keys by default:
T = { key1 = "value1", key2 = false }
-- String keys can use js-like dot notation:
print(T.key1) -- Prints "value1".
print(T.key2) -- Prints "false".
T.newKey = {} -- Adds a new key/value pair.
T.key2 = nil -- Removes key2 from the table.
print(T.key2) -- Prints "nil".
-- Literal notation for any (non-nil) values as key:
U = { ["@!#"] = "gbert", [{}] = 1729, [6.28] = "tau" }
print(U[6.28]) -- Prints "tau"
-- Key matching is basically by value for numbers
-- and strings, but by identity for tables.
A = U["@!#"] -- Now A = "gbert".
B = U[{}] -- We might expect 1729, but it's nil:
-- B = nil since the lookup fails. It fails
-- because the key we used is not the same object
-- as the one used to store the original value. so
-- strings & numbers are more portable keys.
-- A one-table-param function call needs no parens:
function H(x) print(x.key1) end
H { key1 = "sonmi~451" } -- Prints "sonmi~451"
for key, val in pairs(U) do -- Table iteration.
print(key, val)
end
-- _G is a special table of all globals.
print(_G["_G"] == _G) -- Prints "true".
-- Using tables as lists / arrays:
-- List literals implicitly set up int keys:
V = { "value1", "value2", 1.21, "gigawatts" }
for i = 1, #V do -- #V is the size of V for lists.
print(V[i]) -- Indices start at 1 !! SO CRAZY!
end
-- A 'list' is not a real type. V is just a table
-- with consecutive integer keys, treated as a list.
----------------------------------------------------
-- 3.1 Metatables and metamethods.
----------------------------------------------------
-- A table can have a metatable that gives the table
-- operator-overloadish behavior. Later we'll see
-- how metatables support js-prototype behavior.
F1 = { a = 1, b = 2 } -- Represents the fraction a/b.
F2 = { a = 2, b = 3 }
-- This would fail:
-- 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 __add(F1, F2) on F1's metatable
-- F1, F2 have no key for their metatable, unlike
-- prototypes in js, so you must retrieve it as in
-- getmetatable(F1). The metatable is a normal table
-- with keys that Lua knows about, like __add.
-- But the next line fails since a has no metatatble:
-- T = S + S
-- Class-like patterns given below would fix this.
-- An __index on a metatable overloads dot lookups:
DefaultFavs = { animal = "gru", Food = "donuts" }
MyFavs = { food = "pizza" }
setmetatable(MyFavs, { __index = DefaultFavs })
EatenBy = MyFavs.animal -- works! thanks, metatable
-- Direct table lookups that fail will retry using
-- the metatable's __index value, and this recurses.
-- An __index value can also be a function(tbl, key)
-- for more customized lookups.
-- Values of __index, add, .. are called metamethods.
-- Full list. Here a is a table with the metamethod.
-- __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 == 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(...)
2) Class-like tables and inheritance
----------------------------------------------------
-- 3.2 Class-like tables and inheritance.
----------------------------------------------------
-- Classes aren't built in; there are different ways
-- to make them using tables and metatables.
-- Explanation for this example is below it.
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 acts like a class; it's really a table.
-- 2. function tablename:fn(...) is the same as
-- function tablename.fn(self, ...)
-- The : just adds a first arg called self.
-- Read 7 & 8 below for how self gets its value.
-- 3. NewObj will be an instance of class Dog.
-- 4. self = the class being instantiated. Often
-- self = Dog, but inheritance can change it.
-- NewObj gets self's functions when we set both
-- NewObj's metatable and self's __index to self.
-- 5. Reminder: setmetatable returns its first arg.
-- 6. The : works as in 2, but this time we expect
-- self to be an instance instead of a class.
-- 7. Same as Dog.new(Dog), so self = Dog in new().
-- 8. Same as MrDog.makeSound(MrDog); self = MrDog.
-- Below is same with above.
Dog = {}
function Dog.new(table) -- function Dog:new()
NewObj = { sound = "woof" }
table.__index = table -- self.__index = self
return setmetatable(NewObj, table) -- return setmetatable(NewObj, self)
end
function Dog.makeSound(table) -- function Dog:makeSound()
print("I say " .. table.sound) -- print("I say " .. self.sound)
end
MrDog = Dog.new(Dog) -- MrDog = Dog:new()
MrDog.makeSound(MrDog) -- MrDog:makeSound()
----------------------------------------------------
-- Inheritance example:
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 gets Dog's methods and variables.
-- 2. self has a 'sound' key from new(), see 3.
-- 3. Same as LoudDog.new(LoudDog), and converted to
-- Dog.new(LoudDog) as LoudDog has no 'new' key,
-- but does have __index = Dog on its metatable.
-- Result: Seymour's metatable is LoudDog, and
-- LoudDog.__index = LoudDog. So Seymour.key will
-- = Seymour.key, LoudDog.key, Dog.key, whichever
-- table is the first with the given key.
-- 4. The 'makeSound' key is found in LoudDog; this
-- is the same as LoudDog.makeSound(Seymour).
-- Below is same with above.
LoudDog = Dog.new(Dog) -- LoudDog = Dog:new()
function LoudDog.makeSound(table) -- function LoudDog:makeSound()
S = table.sound .. " " -- S = self.sound .. " "
print(S .. S .. S)
end
Seymour = LoudDog.new(LoudDog) -- Seymour = LoudDog:new()
Seymour.makeSound(Seymour) -- Seymour:makeSound()
-- If needed, a subclass's new() is like the base's:
function LoudDog:new()
NewObj = {}
-- set up NewObj
self.__index = self
return setmetatable(NewObj, self)
end
References