Lua Academy · 课时

嵌套表和结构化数据

构建嵌套结构并访问深层字段。

第 4 / 4 课12 个步骤

嵌套表和结构化数据 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。

嵌套表基础

表可以将其他表作为值,从而创建任意深度的结构。可以通过连续使用点号或方括号访问嵌套字段。中间缺少表会导致错误——访问深层字段前,请始终确保父表存在。

local company = {
  name = "CoddyKit",
  address = {
    city = "Istanbul",
    country = "Turkey",
    zip = "34000"
  },
  employees = 25
}

print(company.name)            -- CoddyKit
print(company.address.city)    -- Istanbul
print(company["address"]["zip"]) -- 34000

安全访问深层字段

在 nil 值上访问字段会因“尝试索引 nil 值”而崩溃。可以使用 and 的短路特性进行保护:t and t.a and t.a.b。有些代码库会定义 get(t, ...) 辅助函数,以安全地遍历一串键。

local function get(t, ...)
  local cur = t
  for _, key in ipairs({...}) do
    if type(cur) ~= "table" then return nil end
    cur = cur[key]
  end
  return cur
end

local data = {user = {profile = {age = 30}}}
print(get(data, "user", "profile", "age"))    -- 30
print(get(data, "user", "missing", "field"))  -- nil

记录数组

一种常见模式是由表(记录)组成的数组。每个元素都是一个带有命名字段的表。使用 ipairs 遍历,并按名称访问字段。这相当于 Lua 中的对象数组或结构体列表。

local users = {
  {id=1, name="Alice", score=95},
  {id=2, name="Bob",   score=87},
  {id=3, name="Carol", score=91},
}

for _, user in ipairs(users) do
  print(user.id, user.name, user.score)
end

动态构建嵌套表

通过按需创建内部表,可以逐步构建嵌套结构。在向表中插入内容前,请始终先初始化该表。一个常见错误是忘记先创建内部表,随后却尝试索引 nil。

local tree = {}
tree.root = {value = 10}
tree.root.left = {value = 5}
tree.root.right = {value = 15}
tree.root.left.left = {value = 2}

print(tree.root.value)            -- 10
print(tree.root.left.value)       -- 5
print(tree.root.left.left.value)  -- 2

将表用作类似 JSON 的配置

Lua 表经常用于配置文件,类似于 JSON 或 YAML。该文件返回一个表,应用程序使用 dofile() 或 require() 加载它。嵌套表表示嵌套的配置部分。

-- config.lua (returned table)
local config = {
  server = {
    host = "0.0.0.0",
    port = 8080,
    ssl  = false,
  },
  db = {
    host = "localhost",
    name = "appdb",
    pool_size = 10,
  },
  logging = { level = "info", file = "/var/log/app.log" }
}

print(config.server.port)      -- 8080
print(config.db.pool_size)    -- 10

用邻接表表示图

可以将图表示为由表组成的表:每个键是一个节点,其值是邻居节点列表。对于稀疏图,这种邻接表表示方式节省内存,也便于在 Lua 中使用。

local graph = {
  A = {"B", "C"},
  B = {"A", "D"},
  C = {"A", "D"},
  D = {"B", "C"},
}

-- BFS from A
local visited = {A=true}
local queue = {"A"}
while #queue > 0 do
  local node = table.remove(queue, 1)
  io.write(node .. " ")
  for _, neighbor in ipairs(graph[node]) do
    if not visited[neighbor] then
      visited[neighbor] = true
      queue[#queue+1] = neighbor
    end
  end
end
print()  -- A B C D

修改嵌套字段

访问父表并为相应的键赋值,即可修改嵌套字段。由于表是按引用传递的,任何接收表的函数都可以直接修改其内容——调用者可以看到这些更改。

local player = {name="Hero", stats={hp=100, mp=50, atk=20}}

local function takeDamage(p, dmg)
  p.stats.hp = math.max(0, p.stats.hp - dmg)
end

takeDamage(player, 35)
print(player.stats.hp)   -- 65

player.stats.atk = player.stats.atk + 5
print(player.stats.atk)  -- 25

序列化嵌套表

将嵌套表转换为字符串有助于调试或记录日志。简单的递归序列化器可以处理任意深度,但无法处理循环(循环引用)。

local function serialize(val, indent)
  indent = indent or 0
  local pad = string.rep("  ", indent)
  if type(val) ~= "table" then
    return tostring(val)
  end
  local parts = {"{"}
  for k, v in pairs(val) do
    parts[#parts+1] = pad.."  "..tostring(k).."="..serialize(v,indent+1)
  end
  parts[#parts+1] = pad.."}"
  return table.concat(parts, "\n")
end
local data = {x=1, nested={y=2, z=3}}
print(serialize(data))

稀疏矩阵

以朴素方式存储二维矩阵时,大型稀疏输入会浪费内存。请在嵌套表中只存储非零条目:matrix[row][col] = value。当大多数值为零时,这种方式可以高效地表示超大矩阵。

local function newMatrix()
  return setmetatable({}, {__index=function(t,k)
    local row = {}
    rawset(t, k, row)
    return row
  end})
end

local m = newMatrix()
m[1][1] = 5
m[3][7] = 12
m[100][200] = 99

print(m[1][1])    -- 5
print(m[3][7])    -- 12
print(m[2][5])    -- nil (sparse: no value)

深度相等检查

表使用 == 按引用进行比较,而不是按值比较。若要结构化地比较嵌套表,请编写递归相等函数,比较每个键值对,并递归处理嵌套表。

local function deepEqual(a, b)
  if type(a) ~= type(b) then return false end
  if type(a) ~= "table" then return a == b end
  for k, v in pairs(a) do
    if not deepEqual(v, b[k]) then return false end
  end
  for k in pairs(b) do
    if a[k] == nil then return false end
  end
  return true
end
print(deepEqual({1,{2,3}},{1,{2,3}}))  -- true
print(deepEqual({1,{2,3}},{1,{2,4}}))  -- false

快速检查

当访问 t.a.b 且 t.a 为 nil 时会发生什么?

回顾:嵌套表

总结:

  • 表可以任意嵌套:t.a.b.c
  • 保护深层访问:t and t.a and t.a.b
  • 表是引用类型——函数会直接修改表的内容
  • 记录数组:{{name=...},{name=...}}
  • 稀疏矩阵:使用嵌套表提高内存效率
  • 深度相等比较需要递归处理
免费开始

用 AI 导师学习 Lua — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
40
课程
159

常见问题解答

「嵌套表和结构化数据」课时是免费的吗?

是的 — 「嵌套表和结构化数据」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。

「嵌套表和结构化数据」这节课中我会学到什么?

构建嵌套结构并访问深层字段。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Lua Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「嵌套表和结构化数据」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Lua Academy 课中编写并运行代码吗?

能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 将表用作数组
  2. 将表用作字典
  3. 使用 pairs 和 ipairs 遍历表
  4. 嵌套表和结构化数据
← 返回 Lua Academy