Lua表模式Skill LuaTablesPatterns

这个技能专注于Lua编程语言中表的使用模式,涵盖数组、字典、元表、面向对象编程等核心概念,用于构建高效、灵活的Lua应用程序。关键词:Lua, 表, 数据结构, 数组, 字典, 元表, 面向对象, 编程模式, 性能优化。

游戏开发 0 次安装 0 次浏览 更新于 3/25/2026

名称: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

根据需要实现数据结构以满足特定算法需求,而不是普遍使用。

最佳实践

  1. 使用局部变量处理表,以提高性能和避免全局命名空间污染。
  2. 预分配表当大小已知时,减少重新分配开销。
  3. 优先ipairs用于数组和pairs用于字典,以匹配迭代语义。
  4. 谨慎使用元表因为它们增加开销;仅在需要时使用。
  5. 避免数组中的空洞(nil值),因为它们破坏长度操作符和迭代。
  6. 在紧循环中缓存table.insert和table.remove以提高性能。
  7. 使用rawget和rawset在构建元表时绕过元方法。
  8. 优先弱表用于缓存以允许垃圾回收未使用条目。
  9. 在注释中记录表结构用于复杂嵌套表。
  10. 使用一致的键类型以避免字符串和数字键混淆。

常见陷阱

  1. 忘记1基索引导致从其他语言翻译时出现偏移错误。
  2. **在字典上使用#**返回错误长度,因为它只计算数组部分。
  3. 在数组中创建空洞使用nil值破坏长度操作符和ipairs。
  4. 不检查字典中的nil键导致遗漏值。
  5. 迭代期间修改表导致不可预测行为和缺失元素。
  6. 过度使用元表增加性能开销而无明显好处。
  7. 混淆.和:语法用于方法调用导致错误的self参数。
  8. 递归函数中不处理空表导致无限循环。
  9. 使用表作为布尔值因为空表在Lua中为真。
  10. 浅复制嵌套表留下对嵌套结构的引用。

何时使用此技能

在Lua开发中应用表模式,因为表是主要数据结构。

使用数组处理带数字索引的顺序集合和顺序访问模式。

利用字典进行键值存储、查找和值间映射。

应用元表当实现运算符重载、属性访问控制或DSL时。

当构建需要封装和继承的复杂系统时实现OOP模式。

当需要超越基本表的特定算法属性时使用自定义数据结构。

资源