区别:在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。
元表(Metatable),可以关联table的行为,并且与元方法对应关联
前言
注意:
table使用关联型数组,不能使用nil当作值,其他任意值都是可以的。
table表也是不限制大小的。
table索引从1开始。
提示:以下是本篇文章正文内容,下面案例可供参考
一、table(表)和元表是什么?
table:table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
元表:对table进行操作,对两个表进行相加
二、使用步骤
1.创建table(表)
代码如下(示例):
-- 普通表
local t = { 1, 2, 3, 4, 5 }
--遍历表的键值对
for k,v in pairs(t) do
print("custom table:---"..k,v)
end
--table中可以放任何类型的东西
t = {
function() return 123 end,
function() print("abc") end,
function(a, b) return a + b end,
function() print("hello world") end,
}
t[1]()
t[2]()
t[3](1, 2)
t[4]()
functionList = {
function(a, b)
return a * b
end,
function(a, b)
return a - b
end,
function(a)
return -a
end
}
a, b = 1, 2
print(functionList[1](a, b))
print(functionList[2](a, b))
print(functionList[3](a))
2.使用表的元素
代码如下(示例):
local table = { 1, 2, 3, 4, 5 }
--获取元素
print(t[1], t[2], t[3], t[4], t[5])
--指定元素值
t[6] = 6
print(t[6])
-- 移除引用
table = nil
--table下标的定义
t = { 6, 7, 8, 9 }
--上面和下面的代码等价
t = {
[1] = 6,
[2] = 7,
[3] = 8,
[4] = 9,
}
--甚至你可以跳过某些下标
t = {
[1] = 6,
[3] = 7,
[5] = 8,
[7] = 9,
}
print(t[7])
--输出9
--在声明后赋予元素值也是可以的
t = {} --空的table
t[101] = 10
print(t[101])
--输出10
t = {
[1] = 123,
[13] = "abc",
[666] = "666"
}
print("下标为1的元素:", t[1], type(t[1]))
print("下标为13的元素:", t[13], type(t[13]))
print("下标为666的元素:", t[666], type(t[666]))
--下标进阶
t = {
["apple"] = 10,
banana = 12,
pear = 6,
}
--使用["下标"] = 值
--和 下标 = 值
--都是正确写法
--当第二种方式有歧义时,应该用第一种方式
--可以用下面两种方式访问:
print(t["apple"])
--输出10
print(t.apple)
--输出10
print(t.banana)
print(t.pear)
--lua全局变量和table
--在Lua中,所有的全局变量全部被存放在了一个大table中,这个table名为:_G
n = 6
print(_G.n)
t = {
[10] = 100,
num = 12,
abc = {
[1] = "a",
[2] = "b",
[3] = "abcd",
[4] = "e",
},
a = {
b = {
c = 789
}
}
}
print("t[10]可获得number类型数据100:", t[10], type(t[10]))
print("t.num可获得number类型数据12:", t.num, type(t.num))
print("t.abc[3]可获得string类型数据abcd:", t.abc[3], type(t.abc[3]))
print("t.a.b.c可获得number类型数据789:", t.a.b.c, type(t.a.b.c))
3.关联元表
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable)。
代码如下(示例):
-- 普通表
local t = { 1, 2, 3, 4, 5 }
for k,v in pairs(t) do
print("custom table:---"..k,v)
end
-- 通过setmetatable函数设置元表
local mymetable = {}
local mytable = setmetatable(t, mymetable)--把 mymetatable 设为 t 的元表
--设置的时候可以写成一行
local mytable1 = setmetatable({}, {})
print(getmetatable(mytable)) -- 这会返回 t的地址值
4.元表的元方法
1、__index元方法
- 可以是表或函数。
- 表:在表中查找不存在的键。
- 函数:处理不存在的键查找。
2、__newindex 元方法
- 可以是表或函数。
- 表:将新键值对写入该表。
- 函数:处理对不存在键的赋值操作。
必须是函数的元方法如下:
3、算术运算元方法
__add
、__sub
、__mul
、__div
、__mod
、__pow
、__unm
、__idiv
、__band
、__bor
、__bxor
、__bnot
、__shl
、__shr
- 这些元方法必须是函数,用于定义相应的算术运算。
4、关系运算元方法
__eq
、__lt
、__le
- 这些元方法必须是函数,用于定义相应的比较运算。
5、长度和连接运算元方法
__len
、__concat
- 必须是函数,用于定义长度和连接运算的行为。
6、调用运算元方法
__call
- 必须是函数,用于定义对表进行函数调用的行为。
7、迭代器元方法
__pairs
、__ipairs
- 必须是函数,用于定义自定义迭代行为。
代码如下(示例):
-- 普通表
local t = { 1, 2, 3, 4, 5 }
for k,v in pairs(t) do
print("custom table:---"..k,v)
end
-- 通过setmetatable函数设置元表
local mymetable = {}
local mytable = setmetatable(t, mymetable)--把 mymetatable 设为 t 的元表
--设置的时候可以写成一行
local mytable1 = setmetatable({}, {})
print(getmetatable(mytable)) -- 这会返回 t的地址值
-- __index元方法使用
-- __index元方法可以使用表或者函数来查找值
--[[
当你通过键来访问 table 的时候,如果这个键没有值,
那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。
如果__index包含一个表格,Lua会在表格中查找相应的键
]]
local other = { 1, 2, 3 }
--作为表
local othertable = setmetatable({}, { __index = other })
print(othertable[1])
--结果是1
--作为函数
local mytable2 = setmetatable({key = "value1"}, { __index = function (mytable2,key)
if key == "key1" then
return "value2"
else
return "value3"
end
end
})
print(mytable2.key, mytable2.key1)
--结果是value1 value2
-- __newindex元方法使用
--[[处理对表中不存在的键进行赋值的情况,
当试图给表中不存在的键赋值时,Lua 会调用 __newindex 元方法而不是直接在表中创建新的键值对。
__newindex 元方法接受三个参数:1. table 2. key 3. value
]]
local fallback = {}
--作为表
local t = setmetatable({}, {__newindex = fallback})
t.a = 1 -- 这个键值对会被设置到 `fallback` 表中
print(t.a) -- 输出 nil,因为 `t` 表中没有 `a` 键
print(fallback.a) -- 输出 1,因为 `a` 键被设置到了 `fallback` 表中
--作为函数
local t = setmetatable({}, {
__newindex = function(table, key, value)
print("Attempt to set " .. key .. " to " .. value)
rawset(table, key, value) -- 使用 rawset 直接在表中设置键值对,绕过元方法
end
})
t.a = 1 -- 输出 "Attempt to set a to 1"
print(t.a) -- 输出 1
--__tostring 元方法
--用于在将表转换为字符串时提供自定义行为。
local t1 = setmetatable({}, {
__tostring = function(table)
return "This is a table"
end
})
print(t1) -- 输出 "This is a table"
--__add 元方法
--用于定义两个表相加时的行为。
local t2 = {value = 10}
local t3 = {value = 20}
setmetatable(t2, {
__add = function(a, b)
return {value = a.value + b.value}
end
})
local t4 = t2 + t3
print(t4.value) -- 输出 30
--__call 元方法
--用于在将表当作函数调用时提供自定义行为。
local t5 = setmetatable({}, {
__call = function(table, arg)
return "Called with argument: " .. arg
end
})
print(t5("hello")) -- 输出 "Called with argument: hello"
--__len 元方法
--用于定义表长度的计算行为。
local t6 = {1, 2, 3, 4, 5}
setmetatable(t6, {
__len = function(table)
return #table * 2
end
})
print(#t6) -- 输出 10
--__eq, __lt, __le 元方法
--用于定义相等性、小于和大于比较的逻辑。
local t7 = {value = 10}
local t8 = {value = 10}
setmetatable(t7, {
__eq = function(a, b)
return a.value == b.value
end
})
print(t7 == t8) -- 输出 true
--__concat 元方法
--用于定义表的连接操作行为。
local t9 = {value = "hello"}
local t10 = {value = "world"}
setmetatable(t9, {
__concat = function(a, b)
return a.value .. " " .. b.value
end
})
print(t9 .. t10) -- 输出 "hello world"
--__pairs 和 __ipairs 元方法
--用于自定义表的迭代行为。
local t11 = {1, 2, 3, key1 = "value1", key2 = "value2"}
setmetatable(t11, {
__pairs = function(table)
local keys = {}
for k in next, table do
table.insert(keys, k)
end
table.sort(keys)
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], table[keys[i]]
end
end
end
})
for k, v in pairs(t11) do
print(k, v)
end
--实现只读表
local readonly = {}
local mt = {
__index = function(table, key)
return readonly[key]
end,
__newindex = function(table, key, value)
error("Attempt to modify a readonly table")
end
}
local t12 = setmetatable({}, mt)
readonly.a = 1
print(t12.a) -- 输出 1
t12.a = 2 -- 报错 "Attempt to modify a readonly table"
5.表的操作
代码如下(示例):
1、创建和初始化表
-- 创建一个空表
local myTable = {}
-- 初始化表时添加一些键值对
local person = {
name = "John",
age = 30,
isEmployed = true
}
-- 数组(序列)
local fruits = { "apple", "banana", "cherry" }
2、访问和修改表的元素
-- 访问表中的元素
print(person.name) -- 输出 "John"
print(fruits[1]) -- 输出 "apple"
-- 修改表中的元素
person.age = 31
fruits[2] = "blueberry"
print(person.age) -- 输出 31
print(fruits[2]) -- 输出 "blueberry"
3、插入和删除元素
-- 插入元素
table.insert(fruits, "date") -- 在末尾插入 "date"
table.insert(fruits, 2, "elderberry") -- 在索引 2 处插入 "elderberry"
-- 删除元素
table.remove(fruits) -- 删除最后一个元素
table.remove(fruits, 2) -- 删除索引 2 处的元素
--还有一些别的方法..不一一举例了
-- 打印表中的所有元素
for i, v in ipairs(fruits) do
print(i, v)
end
4、遍历表
-- 遍历数组
for i, v in ipairs(fruits) do
print(i, v)
end
-- 遍历字典
for k, v in pairs(person) do
print(k, v)
end
5、元表和元方法
-- 创建一个简单的元表
local mt = {
__index = function(table, key)
return "Key not found"
end
}
-- 创建一个表并设置元表
local myTable = setmetatable({}, mt)
print(myTable.someKey) -- 输出 "Key not found"
6、合并表
local function mergeTables(t1, t2)
for k, v in pairs(t2) do
t1[k] = v
end
end
local t1 = {a = 1, b = 2}
local t2 = {c = 3, d = 4}
mergeTables(t1, t2)
for k, v in pairs(t1) do
print(k, v)
end
7、表的长度
local t = {1, 2, 3, 4, 5}
print(#t) -- 输出 5
8、多重索引
local matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
print(matrix[2][3]) -- 输出 6
9、表的克隆
local function cloneTable(orig)
local copy = {}
for k, v in pairs(orig) do
if type(v) == "table" then
copy[k] = cloneTable(v)
else
copy[k] = v
end
end
return copy
end
local t1 = {a = 1, b = {c = 2, d = 3}}
local t2 = cloneTable(t1)
t2.b.c = 10
print(t1.b.c) -- 输出 2
print(t2.b.c) -- 输出 10
10、表排序
local numbers = {5, 2, 9, 1, 7, 6}
table.sort(numbers)
for i, v in ipairs(numbers) do
print(i, v)
end
11、表的元方法
local t = {10, 20, 30}
-- 设置元表
setmetatable(t, {
__add = function(a, b)
local result = {}
for i = 1, #a do
result[i] = a[i] + b[i]
end
return result
end
})
local t2 = {1, 2, 3}
local t3 = t + t2 -- 使用元方法
for i, v in ipairs(t3) do
print(i, v) -- 输出 11, 22, 33
end
12、表的默认值
local mt = {
__index = function(table, key)
return 0 -- 默认值
end
}
local t = setmetatable({}, mt)
print(t.someKey) -- 输出 0
总结
在 Lua 中创建和操作表,包括初始化、访问、修改、插入、删除、遍历、合并、克隆、排序等操作。通过熟练掌握这些操作,你可以在 Lua 中高效地处理各种数据结构和任务。