名称:Lua表模式 用户可调用:否 描述:当Lua表作为通用数据结构时使用,包括数组、字典、对象、元表、面向对象模式、数据结构和高级表操作,用于构建灵活、高效的Lua应用程序。 允许工具:[]
Lua表模式
引言
表是Lua唯一的复合数据结构,可作为数组、字典、对象、模块等。这种多功能性使表成为Lua编程的基础,通过创造性地使用表特性和元表,几乎可以实现任何数据结构。
表使用关联数组,可以用除nil和NaN之外的任何Lua值索引。数组式表使用从1开始的连续整数索引,而字典式表使用任意键。元表支持运算符重载和高级行为。
本技能涵盖表基础、数组和字典模式、元表、面向对象编程、常见数据结构、性能优化和惯用表操作技术。
表基础
表将数组和哈希映射组合成单一多功能数据结构,采用1基索引。
-- 创建空表
local t1 = {}
local t2 = table.create(100) -- 预分配(LuaJIT)
-- 数组式表(从1开始的连续整数)
local array = {10, 20, 30, 40, 50}
print(array[1]) -- 10
print(array[5]) -- 50
print(#array) -- 5(长度操作符)
-- 字典式表(任意键类型)
local dict = {
name = "Alice",
age = 30,
email = "alice@example.com"
}
print(dict.name) -- 点表示法
print(dict["age"]) -- 括号表示法
-- 混合数组和字典
local mixed = {
10, 20, 30, -- 数组部分:[1]=10, [2]=20, [3]=30
name = "Bob",
age = 25
}
print(mixed[2]) -- 20
print(mixed.name) -- "Bob"
-- 构造器语法变体
local point = {x = 10, y = 20}
local point2 = {["x"] = 10, ["y"] = 20} -- 等效
-- 嵌套表
local nested = {
user = {
name = "Charlie",
address = {
city = "Seattle",
zip = "98101"
}
}
}
print(nested.user.address.city) -- "Seattle"
-- 表插入和移除
local list = {}
table.insert(list, "first")
table.insert(list, "second")
table.insert(list, 2, "middle") -- 在位置2插入
print(list[1]) -- "first"
print(list[2]) -- "middle"
print(list[3]) -- "second"
local removed = table.remove(list, 2) -- 在位置2移除
print(removed) -- "middle"
-- 表迭代
-- Pairs:所有键(无序)
for key, value in pairs(dict) do
print(key, value)
end
-- Ipairs:整数键按顺序(1, 2, 3, ...)
for index, value in ipairs(array) do
print(index, value)
end
-- 手动迭代
for i = 1, #array do
print(i, array[i])
end
-- 表复制(浅复制)
local original = {1, 2, 3, x = 10}
local copy = {}
for k, v in pairs(original) do
copy[k] = v
end
-- 表连接
local fruits = {"apple", "banana", "cherry"}
local str = table.concat(fruits, ", ")
print(str) -- "apple, banana, cherry"
-- 排序
local numbers = {5, 2, 8, 1, 9}
table.sort(numbers)
-- numbers现在是{1, 2, 5, 8, 9}
-- 自定义排序
local people = {
{name = "Alice", age = 30},
{name = "Bob", age = 25},
{name = "Charlie", age = 35}
}
table.sort(people, function(a, b)
return a.age < b.age
end)
表高效地结合数组和哈希表,实现根据使用模式自动优化存储。
数组模式
Lua数组使用1基索引,并通过表的数组部分提供高效的顺序访问。
-- 创建数组
local empty = {}
local numbers = {10, 20, 30, 40, 50}
local strings = {"hello", "world", "lua"}
-- 数组长度
print(#numbers) -- 5
-- 追加元素
numbers[#numbers + 1] = 60
table.insert(numbers, 70)
-- 前置(开销大)
table.insert(numbers, 1, 0)
-- 移除元素
local last = table.remove(numbers) -- 移除并返回最后一个
local first = table.remove(numbers, 1) -- 移除并返回第一个
-- 数组操作
function map(array, fn)
local result = {}
for i = 1, #array do
result[i] = fn(array[i])
end
return result
end
local doubled = map(numbers, function(x) return x * 2 end)
function filter(array, predicate)
local result = {}
for i = 1, #array do
if predicate(array[i]) then
table.insert(result, array[i])
end
end
return result
end
local evens = filter(numbers, function(x) return x % 2 == 0 end)
function reduce(array, fn, initial)
local accumulator = initial
for i = 1, #array do
accumulator = fn(accumulator, array[i])
end
return accumulator
end
local sum = reduce(numbers, function(acc, x) return acc + x end, 0)
-- 数组切片
function slice(array, start_idx, end_idx)
local result = {}
end_idx = end_idx or #array
for i = start_idx, end_idx do
table.insert(result, array[i])
end
return result
end
local subset = slice(numbers, 2, 4)
-- 数组反转
function reverse(array)
local result = {}
for i = #array, 1, -1 do
table.insert(result, array[i])
end
return result
end
-- 原地反转
function reverse_in_place(array)
local n = #array
for i = 1, math.floor(n / 2) do
array[i], array[n - i + 1] = array[n - i + 1], array[i]
end
end
-- 数组扁平化
function flatten(array)
local result = {}
for i = 1, #array do
if type(array[i]) == "table" then
local nested = flatten(array[i])
for j = 1, #nested do
table.insert(result, nested[j])
end
else
table.insert(result, array[i])
end
end
return result
end
local nested = {1, {2, 3}, {4, {5, 6}}}
local flat = flatten(nested) -- {1, 2, 3, 4, 5, 6}
-- 数组分块
function chunk(array, size)
local result = {}
for i = 1, #array, size do
local chunk = {}
for j = i, math.min(i + size - 1, #array) do
table.insert(chunk, array[j])
end
table.insert(result, chunk)
end
return result
end
local chunked = chunk({1, 2, 3, 4, 5, 6, 7}, 3)
-- {{1, 2, 3}, {4, 5, 6}, {7}}
-- 唯一值
function unique(array)
local seen = {}
local result = {}
for i = 1, #array do
if not seen[array[i]] then
seen[array[i]] = true
table.insert(result, array[i])
end
end
return result
end
-- 数组交集
function intersection(a, b)
local set = {}
for i = 1, #a do
set[a[i]] = true
end
local result = {}
for i = 1, #b do
if set[b[i]] then
table.insert(result, b[i])
end
end
return result
end
使用数组处理带数字索引的顺序数据,利用Lua对连续整数键的优化。
字典和集合模式
字典表使用任意键进行快速查找,而集合使用键和真值。
-- 字典创建
local user = {
id = 1,
name = "Alice",
email = "alice@example.com",
age = 30
}
-- 动态键访问
local key = "name"
print(user[key]) -- "Alice"
-- 添加和修改
user.city = "Seattle"
user.age = 31
-- 移除键
user.age = nil
-- 检查键是否存在
if user.name then
print("名称存在")
end
-- 计算键数
function table_length(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
print(table_length(user))
-- 合并字典
function merge(t1, t2)
local result = {}
for k, v in pairs(t1) do
result[k] = v
end
for k, v in pairs(t2) do
result[k] = v
end
return result
end
-- 深复制
function deep_copy(obj)
if type(obj) ~= 'table' then return obj end
local copy = {}
for k, v in pairs(obj) do
copy[deep_copy(k)] = deep_copy(v)
end
return setmetatable(copy, getmetatable(obj))
end
-- 集合实现
local Set = {}
function Set.new(list)
local set = {}
for i = 1, #list do
set[list[i]] = true
end
return set
end
function Set.add(set, value)
set[value] = true
end
function Set.remove(set, value)
set[value] = nil
end
function Set.contains(set, value)
return set[value] == true
end
function Set.union(a, b)
local result = {}
for k in pairs(a) do result[k] = true end
for k in pairs(b) do result[k] = true end
return result
end
function Set.intersection(a, b)
local result = {}
for k in pairs(a) do
if b[k] then result[k] = true end
end
return result
end
function Set.difference(a, b)
local result = {}
for k in pairs(a) do
if not b[k] then result[k] = true end
end
return result
end
function Set.to_list(set)
local list = {}
for k in pairs(set) do
table.insert(list, k)
end
return list
end
-- 使用
local set1 = Set.new({1, 2, 3, 4, 5})
local set2 = Set.new({4, 5, 6, 7, 8})
Set.add(set1, 10)
print(Set.contains(set1, 3)) -- true
local union = Set.union(set1, set2)
local inter = Set.intersection(set1, set2)
-- OrderedDict模式
local OrderedDict = {}
function OrderedDict.new()
return {_keys = {}, _values = {}}
end
function OrderedDict.set(dict, key, value)
if not dict._values[key] then
table.insert(dict._keys, key)
end
dict._values[key] = value
end
function OrderedDict.get(dict, key)
return dict._values[key]
end
function OrderedDict.pairs(dict)
local i = 0
return function()
i = i + 1
local key = dict._keys[i]
if key then
return key, dict._values[key]
end
end
end
字典提供O(1)平均情况查找,并支持灵活的键值存储模式。
元表和元方法
元表通过元方法支持运算符重载、属性访问控制和自定义行为。
-- 基本元表
local t = {}
local mt = {
__index = function(table, key)
return "默认值"
end
}
setmetatable(t, mt)
print(t.anything) -- "默认值"
-- 运算符重载
local Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x, y = y}, Vector)
end
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
function Vector.__sub(a, b)
return Vector.new(a.x - b.x, a.y - b.y)
end
function Vector.__mul(a, scalar)
if type(scalar) == "number" then
return Vector.new(a.x * scalar, a.y * scalar)
end
end
function Vector.__tostring(v)
return string.format("Vector(%d, %d)", v.x, v.y)
end
function Vector.__eq(a, b)
return a.x == b.x and a.y == b.y
end
local v1 = Vector.new(1, 2)
local v2 = Vector.new(3, 4)
local v3 = v1 + v2
print(v3) -- Vector(4, 6)
-- 只读表
function read_only(table)
local proxy = {}
local mt = {
__index = table,
__newindex = function(t, k, v)
error("尝试修改只读表")
end
}
return setmetatable(proxy, mt)
end
local config = read_only({
api_key = "secret",
timeout = 5000
})
-- config.timeout = 10000 -- 错误!
-- 延迟初始化
function lazy_table(constructor)
local cache = {}
local mt = {
__index = function(t, key)
local value = constructor(key)
rawset(t, key, value)
return value
end
}
return setmetatable({}, mt)
end
local fibonacci = lazy_table(function(n)
if n <= 1 then return n end
return fibonacci[n - 1] + fibonacci[n - 2]
end)
print(fibonacci[10]) -- 55
-- 默认值
function table_with_default(default)
local mt = {
__index = function() return default end
}
return setmetatable({}, mt)
end
local counts = table_with_default(0)
counts.apple = counts.apple + 1
counts.banana = counts.banana + 1
-- 方法调用语法
local Object = {}
Object.__index = Object
function Object.new()
return setmetatable({}, Object)
end
function Object:method()
-- self引用实例
print("调用在", self)
end
local obj = Object.new()
obj:method() -- 语法糖:obj.method(obj)
-- __call元方法
local Factory = {}
function Factory.new()
local instance = {count = 0}
local mt = {
__call = function(self, ...)
self.count = self.count + 1
return ...
end
}
return setmetatable(instance, mt)
end
local f = Factory.new()
f("hello") -- 可调用像函数
print(f.count) -- 1
-- 属性跟踪
function tracked_table()
local data = {}
local mt = {
__index = data,
__newindex = function(t, k, v)
print(string.format("设置 %s = %s", k, v))
data[k] = v
end
}
return setmetatable({}, mt)
end
元表支持强大的元编程模式和领域特定语言。
面向对象模式
Lua通过表和元表支持多种OOP方法,从简单原型到类基系统。
-- 原型基OOP
local Animal = {species = "未知"}
function Animal:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function Animal:speak()
print("一些声音")
end
local Dog = Animal:new{species = "犬类"}
function Dog:speak()
print("汪汪!")
end
function Dog:fetch()
print("取物中...")
end
local dog = Dog:new{name = "Buddy"}
dog:speak() -- "汪汪!"
print(dog.species) -- "犬类"
-- 类基OOP
local Class = {}
function Class:new(...)
local instance = setmetatable({}, self)
self.__index = self
if instance.init then
instance:init(...)
end
return instance
end
-- 定义类
local Person = Class:new()
function Person:init(name, age)
self.name = name
self.age = age
end
function Person:greet()
return string.format("你好,我是%s", self.name)
end
function Person:birthday()
self.age = self.age + 1
end
local alice = Person:new("Alice", 30)
print(alice:greet())
alice:birthday()
print(alice.age) -- 31
-- 继承
local Employee = Class:new()
setmetatable(Employee, {__index = Person})
function Employee:init(name, age, company)
Person.init(self, name, age)
self.company = company
end
function Employee:work()
return string.format("%s在%s工作", self.name, self.company)
end
local bob = Employee:new("Bob", 25, "TechCorp")
print(bob:greet()) -- 从Person继承
print(bob:work())
-- 封装闭包
function BankAccount(initial_balance)
local balance = initial_balance or 0
return {
deposit = function(amount)
if amount > 0 then
balance = balance + amount
return true
end
return false
end,
withdraw = function(amount)
if amount > 0 and amount <= balance then
balance = balance - amount
return true
end
return false
end,
get_balance = function()
return balance
end
}
end
local account = BankAccount(100)
account.deposit(50)
account.withdraw(30)
print(account.get_balance()) -- 120
-- 混入
local Flyable = {}
function Flyable:fly()
print(self.name .. " 在飞行")
end
function Flyable:land()
print(self.name .. " 已降落")
end
function mixin(target, source)
for k, v in pairs(source) do
target[k] = v
end
end
local Bird = Person:new()
mixin(Bird, Flyable)
local bird = Bird:new("Eagle", 5)
bird:fly()
-- 多分派模式
local Shape = Class:new()
function Shape:init(type)
self.type = type
end
local Circle = Shape:new()
function Circle:init(radius)
Shape.init(self, "circle")
self.radius = radius
end
local Rectangle = Shape:new()
function Rectangle:init(width, height)
Shape.init(self, "rectangle")
self.width = width
self.height = height
end
-- 访问者模式
local AreaCalculator = {}
function AreaCalculator.visit_circle(circle)
return math.pi * circle.radius * circle.radius
end
function AreaCalculator.visit_rectangle(rect)
return rect.width * rect.height
end
function calculate_area(shape)
local method_name = "visit_" .. shape.type
return AreaCalculator[method_name](shape)
end
根据需求选择OOP模式:原型用于简单层次,类用于结构化系统,闭包用于封装。
常见数据结构
使用表实现经典数据结构以满足特定算法需求。
-- 栈
local Stack = {}
function Stack.new()
return {items = {}}
end
function Stack.push(stack, item)
table.insert(stack.items, item)
end
function Stack.pop(stack)
return table.remove(stack.items)
end
function Stack.peek(stack)
return stack.items[#stack.items]
end
function Stack.is_empty(stack)
return #stack.items == 0
end
-- 队列
local Queue = {}
function Queue.new()
return {items = {}, head = 1, tail = 0}
end
function Queue.enqueue(queue, item)
queue.tail = queue.tail + 1
queue.items[queue.tail] = item
end
function Queue.dequeue(queue)
if queue.head > queue.tail then
return nil
end
local item = queue.items[queue.head]
queue.items[queue.head] = nil
queue.head = queue.head + 1
return item
end
-- 链表
local LinkedList = {}
function LinkedList.new()
return {head = nil, tail = nil, size = 0}
end
function LinkedList.append(list, value)
local node = {value = value, next = nil}
if list.tail then
list.tail.next = node
list.tail = node
else
list.head = node
list.tail = node
end
list.size = list.size + 1
end
function LinkedList.prepend(list, value)
local node = {value = value, next = list.head}
list.head = node
if not list.tail then
list.tail = node
end
list.size = list.size + 1
end
function LinkedList.to_array(list)
local array = {}
local current = list.head
while current do
table.insert(array, current.value)
current = current.next
end
return array
end
-- 二叉搜索树
local BST = {}
function BST.new()
return {root = nil}
end
function BST.insert(tree, value)
local function insert_node(node, value)
if not node then
return {value = value, left = nil, right = nil}
end
if value < node.value then
node.left = insert_node(node.left, value)
elseif value > node.value then
node.right = insert_node(node.right, value)
end
return node
end
tree.root = insert_node(tree.root, value)
end
function BST.search(tree, value)
local function search_node(node, value)
if not node then return false end
if value == node.value then return true end
if value < node.value then
return search_node(node.left, value)
else
return search_node(node.right, value)
end
end
return search_node(tree.root, value)
end
function BST.inorder(tree)
local result = {}
local function traverse(node)
if not node then return end
traverse(node.left)
table.insert(result, node.value)
traverse(node.right)
end
traverse(tree.root)
return result
end
-- 优先队列(二叉堆)
local PriorityQueue = {}
function PriorityQueue.new(comparator)
return {
heap = {},
compare = comparator or function(a, b) return a < b end
}
end
function PriorityQueue.push(pq, item)
table.insert(pq.heap, item)
local i = #pq.heap
while i > 1 do
local parent = math.floor(i / 2)
if pq.compare(pq.heap[i], pq.heap[parent]) then
pq.heap[i], pq.heap[parent] = pq.heap[parent], pq.heap[i]
i = parent
else
break
end
end
end
function PriorityQueue.pop(pq)
if #pq.heap == 0 then return nil end
local result = pq.heap[1]
pq.heap[1] = pq.heap[#pq.heap]
table.remove(pq.heap)
local i = 1
while true do
local left = i * 2
local right = i * 2 + 1
local smallest = i
if left <= #pq.heap and pq.compare(pq.heap[left], pq.heap[smallest]) then
smallest = left
end
if right <= #pq.heap and pq.compare(pq.heap[right], pq.heap[smallest]) then
smallest = right
end
if smallest == i then break end
pq.heap[i], pq.heap[smallest] = pq.heap[smallest], pq.heap[i]
i = smallest
end
return result
end
-- 图(邻接列表)
local Graph = {}
function Graph.new()
return {vertices = {}}
end
function Graph.add_vertex(graph, vertex)
if not graph.vertices[vertex] then
graph.vertices[vertex] = {}
end
end
function Graph.add_edge(graph, from, to, weight)
Graph.add_vertex(graph, from)
Graph.add_vertex(graph, to)
table.insert(graph.vertices[from], {to = to, weight = weight or 1})
end
function Graph.neighbors(graph, vertex)
return graph.vertices[vertex] or {}
end
根据需要实现数据结构以满足特定算法需求,而不是普遍使用。
最佳实践
- 使用局部变量处理表,以提高性能和避免全局命名空间污染。
- 预分配表当大小已知时,减少重新分配开销。
- 优先ipairs用于数组和pairs用于字典,以匹配迭代语义。
- 谨慎使用元表因为它们增加开销;仅在需要时使用。
- 避免数组中的空洞(nil值),因为它们破坏长度操作符和迭代。
- 在紧循环中缓存table.insert和table.remove以提高性能。
- 使用rawget和rawset在构建元表时绕过元方法。
- 优先弱表用于缓存以允许垃圾回收未使用条目。
- 在注释中记录表结构用于复杂嵌套表。
- 使用一致的键类型以避免字符串和数字键混淆。
常见陷阱
- 忘记1基索引导致从其他语言翻译时出现偏移错误。
- **在字典上使用#**返回错误长度,因为它只计算数组部分。
- 在数组中创建空洞使用nil值破坏长度操作符和ipairs。
- 不检查字典中的nil键导致遗漏值。
- 迭代期间修改表导致不可预测行为和缺失元素。
- 过度使用元表增加性能开销而无明显好处。
- 混淆.和:语法用于方法调用导致错误的self参数。
- 递归函数中不处理空表导致无限循环。
- 使用表作为布尔值因为空表在Lua中为真。
- 浅复制嵌套表留下对嵌套结构的引用。
何时使用此技能
在Lua开发中应用表模式,因为表是主要数据结构。
使用数组处理带数字索引的顺序集合和顺序访问模式。
利用字典进行键值存储、查找和值间映射。
应用元表当实现运算符重载、属性访问控制或DSL时。
当构建需要封装和继承的复杂系统时实现OOP模式。
当需要超越基本表的特定算法属性时使用自定义数据结构。