Lua


13.3 表库 (Table Library)


文档摘要

13.3 表库 (Table Library) Lua 表库 (Table Library) 详解与实践指南 1. 表的基本概念回顾 在深入表库之前,我们先快速回顾一下 Lua 表的基础知识。 唯一的数据结构: Lua 中只有表这一种主要的数据结构。它既可以作为关联数组 (键值对集合),也可以作为数字索引数组 (类似其他语言的列表或数组)。 动态性: 表是动态的,可以随时添加、删除键值对,无需预先声明大小。 灵活性: 表的键和值可以是任意 Lua 值类型,包括数字、字符串、表、函数等。 对象和模块的基础: Lua 中的对象和模块都是通过表来实现的。 2. 表库概述 Lua 的表库 ( 库) 提供了一组函数,用于操作表数据,包括插入、删除、排序、查找、连接、复制等常见操作。

13.3 表库 (Table Library)

Lua 表库 (Table Library) 详解与实践指南

1. 表的基本概念回顾

在深入表库之前,我们先快速回顾一下 Lua 表的基础知识。

  • 唯一的数据结构: Lua 中只有表这一种主要的数据结构。它既可以作为关联数组 (键值对集合),也可以作为数字索引数组 (类似其他语言的列表或数组)。

  • 动态性: 表是动态的,可以随时添加、删除键值对,无需预先声明大小。

  • 灵活性: 表的键和值可以是任意 Lua 值类型,包括数字、字符串、表、函数等。

  • 对象和模块的基础: Lua 中的对象和模块都是通过表来实现的。

2. 表库概述

Lua 的表库 (table 库) 提供了一组函数,用于操作表数据,包括插入、删除、排序、查找、连接、复制等常见操作。这些函数都以 table. 前缀开头,例如 table.inserttable.remove 等。

3. 核心表库函数详解与实践

接下来,我们将详细介绍表库中常用的核心函数,并通过代码示例进行实践演示。

3.1 table.insert (list, [pos,] value)

  • 功能: 在列表 list 的指定位置 pos 插入元素 value。如果省略 pos,则默认在列表末尾插入。

  • 参数:

    • list: 目标列表 (必须是一个表)。

    • pos (可选): 插入位置的索引,必须是介于 1 和列表长度之间的整数 (包含边界)。如果省略,默认为 n+1,其中 n 是列表的当前长度。

    • value: 要插入的值。

  • 返回值: 无返回值。函数直接修改传入的表 list

  • 实践示例:

local myTable = {1, 2, 3, 4, 5} -- 在末尾插入 6 table.insert(myTable, 6) print("After inserting 6 at the end:", table.concat(myTable, ", ")) -- 输出: After inserting 6 at the end: 1, 2, 3, 4, 5, 6 -- 在索引 3 的位置插入 10 table.insert(myTable, 3, 10) print("After inserting 10 at index 3:", table.concat(myTable, ", ")) -- 输出: After inserting 10 at index 3: 1, 2, 10, 3, 4, 5, 6 -- 在索引 1 的位置插入 0 table.insert(myTable, 1, 0) print("After inserting 0 at index 1:", table.concat(myTable, ", ")) -- 输出: After inserting 0 at index 1: 0, 1, 2, 10, 3, 4, 5, 6
  • 详解: table.insert 函数用于向数字索引的表中插入元素。当指定插入位置 pos 时,它会将从 pos 位置开始的所有元素向后移动一位,然后将 value 插入到 pos 位置。如果省略 pos,则直接在表的末尾添加元素。

3.2 table.remove (list [, pos])

  • 功能: 移除列表 list 中指定位置 pos 的元素,并返回被移除的元素。如果省略 pos,则默认移除列表末尾的元素。

  • 参数:

    • list: 目标列表 (必须是一个表)。

    • pos (可选): 要移除元素的位置索引,必须是介于 1 和列表长度之间的整数 (包含边界)。如果省略,默认为列表的长度。

  • 返回值: 被移除的元素的值。

  • 实践示例:

local myTable = {0, 1, 2, 10, 3, 4, 5, 6} -- 移除末尾元素 local removedElement1 = table.remove(myTable) print("Removed element from the end:", removedElement1) -- 输出: Removed element from the end: 6 print("Table after removing from end:", table.concat(myTable, ", ")) -- 输出: Table after removing from end: 0, 1, 2, 10, 3, 4, 5 -- 移除索引 4 的元素 (原本是 10) local removedElement2 = table.remove(myTable, 4) print("Removed element at index 4:", removedElement2) -- 输出: Removed element at index 4: 10 print("Table after removing from index 4:", table.concat(myTable, ", ")) -- 输出: Table after removing from index 4: 0, 1, 2, 3, 4, 5 -- 移除索引 1 的元素 (原本是 0) local removedElement3 = table.remove(myTable, 1) print("Removed element at index 1:", removedElement3) -- 输出: Removed element at index 1: 0 print("Table after removing from index 1:", table.concat(myTable, ", ")) -- 输出: Table after removing from index 1: 1, 2, 3, 4, 5
  • 详解: table.remove 函数用于从数字索引的表中移除元素。当指定移除位置 pos 时,它会移除 pos 位置的元素,并将从 pos+1 位置开始的所有元素向前移动一位,以填补空缺。如果省略 pos,则移除表的最后一个元素。函数返回被移除的元素值。

3.3 table.concat (list [, sep [, i [, j]]])

  • 功能: 连接列表 list 中索引从 ij 的所有字符串元素,并使用分隔符 sep 分隔。如果省略 sep,则使用空字符串作为分隔符。如果省略 ij,则默认连接列表中的所有元素。

  • 参数:

    • list: 目标列表 (必须是一个表)。

    • sep (可选): 分隔符字符串,默认为空字符串 ""

    • i (可选): 起始索引,默认为 1。

    • j (可选): 结束索引,默认为列表的长度。

  • 返回值: 连接后的字符串。

  • 实践示例:

local stringTable = {"apple", "banana", "cherry", "date"} -- 连接所有元素,默认分隔符为空字符串 local concatenatedString1 = table.concat(stringTable) print("Concatenated string (no separator):", concatenatedString1) -- 输出: Concatenated string (no separator): applebananacherrydate -- 使用逗号加空格作为分隔符连接所有元素 local concatenatedString2 = table.concat(stringTable, ", ") print("Concatenated string (comma separator):", concatenatedString2) -- 输出: Concatenated string (comma separator): apple, banana, cherry, date -- 连接索引 2 到 3 的元素,使用连字符分隔 local concatenatedString3 = table.concat(stringTable, "-", 2, 3) print("Concatenated string (index 2-3, hyphen separator):", concatenatedString3) -- 输出: Concatenated string (index 2-3, hyphen separator): banana-cherry
  • 详解: table.concat 函数高效地将表中的字符串元素连接成一个字符串。它可以指定分隔符以及连接的元素范围,非常适合构建字符串列表或者格式化输出。注意:table.concat 只能用于连接字符串类型的元素。如果表中包含非字符串类型的元素,将会报错。

3.4 table.sort (list [, comp])

  • 功能: 对列表 list 中的元素进行就地排序。排序算法是不稳定的(即相等元素的相对位置可能会改变)。

  • 参数:

    • list: 目标列表 (必须是一个表)。

    • comp (可选): 一个比较函数,用于自定义排序规则。它接收两个参数 ab,如果 a 应该排在 b 前面,则返回 true,否则返回 false。如果省略 comp,则默认使用小于运算符 < 进行升序排序。

  • 返回值: 无返回值。函数直接修改传入的表 list

  • 实践示例:

local numberTable = {5, 2, 8, 1, 9, 4, 7, 3, 6} -- 默认升序排序 table.sort(numberTable) print("Sorted table (ascending):", table.concat(numberTable, ", ")) -- 输出: Sorted table (ascending): 1, 2, 3, 4, 5, 6, 7, 8, 9 -- 自定义降序排序比较函数 local function descendingComp(a, b) return a > b -- a > b 返回 true 表示 a 应该排在 b 前面 (降序) end table.sort(numberTable, descendingComp) print("Sorted table (descending):", table.concat(numberTable, ", ")) -- 输出: Sorted table (descending): 9, 8, 7, 6, 5, 4, 3, 2, 1 local stringTableToSort = {"banana", "apple", "cherry", "date"} -- 默认字符串排序 (字典序) table.sort(stringTableToSort) print("Sorted string table (lexicographical):", table.concat(stringTableToSort, ", ")) -- 输出: Sorted string table (lexicographical): apple, banana, cherry, date -- 自定义字符串长度排序 local function sortByLength(a, b) return string.len(a) < string.len(b) -- 长度较短的字符串排在前面 end table.sort(stringTableToSort, sortByLength) print("Sorted string table (by length):", table.concat(stringTableToSort, ", ")) -- 输出: Sorted string table (by length): date, apple, banana, cherry
  • 详解: table.sort 函数提供了强大的排序功能。默认情况下,它使用小于运算符 < 进行升序排序,适用于数字和字符串等基本类型。通过提供自定义的比较函数 comp,可以实现更复杂的排序逻辑,例如降序排序、按照对象属性排序等。需要注意的是,table.sort 是就地排序,会直接修改原始表。

3.5 table.move (a1, f, e, t, a2)

  • 功能: 将表 a1 中索引从 fe 的元素移动到表 a2 的索引 t 开始的位置。可以移动到 a2 的相同位置,即 a2 可以是 a1

  • 参数:

    • a1: 源表。

    • f: 源表起始索引。

    • e: 源表结束索引。

    • t: 目标表起始索引。

    • a2: 目标表。

  • 返回值: 返回目标表 a2

  • 实践示例:

local sourceTable = {1, 2, 3, 4, 5, 6} local targetTable = {10, 20, 30} -- 将 sourceTable 的索引 2 到 4 (即 2, 3, 4) 移动到 targetTable 的索引 2 开始的位置 table.move(sourceTable, 2, 4, 2, targetTable) print("Source Table after move:", table.concat(sourceTable, ", ")) -- 输出: Source Table after move: 1, 5, 6, 4, 5, 6 (注意:源表被修改,移动的元素位置被后面的元素覆盖,但超出范围的元素值未被清空,这是实现细节,不应依赖) print("Target Table after move:", table.concat(targetTable, ", ")) -- 输出: Target Table after move: 10, 2, 3, 4, 30 -- 将 sourceTable 的索引 1 到 2 移动到 sourceTable 自身索引 3 开始的位置 table.move(sourceTable, 1, 2, 3, sourceTable) print("Source Table after self-move:", table.concat(sourceTable, ", ")) -- 输出: Source Table after self-move: 5, 6, 1, 2, 3, 4 (注意:原表索引 3, 4 被覆盖)
  • 详解: table.move 函数提供了一种高效的方式来移动表中的一部分元素到另一个表或同一个表的其他位置。这在需要重组表结构或者进行数据转移时非常有用。需要特别注意的是,table.move 会修改源表 a1,被移动的元素在源表中的位置会被覆盖。 在进行自移动 (移动到自身) 时,需要仔细考虑索引的重叠和覆盖问题。

3.6 table.pack (...)

  • 功能: 将所有参数打包到一个新表中,并将参数的数量保存在表的字段 n 中。

  • 参数: 任意数量的 Lua 值。

  • 返回值: 一个新的表,包含所有参数和一个额外的字段 n 记录参数数量。

  • 实践示例:

local packedTable = table.pack(10, "hello", true, {key = "value"}) print("Packed Table:", packedTable) -- 输出: Packed Table: table: 0x... (表的内存地址) print("Packed Table n:", packedTable.n) -- 输出: Packed Table n: 4 print("Packed Table [1]:", packedTable[1]) -- 输出: Packed Table [1]: 10 print("Packed Table [2]:", packedTable[2]) -- 输出: Packed Table [2]: hello print("Packed Table [3]:", packedTable[3]) -- 输出: Packed Table [3]: true print("Packed Table [4]:", packedTable[4]) -- 输出: Packed Table [4]: table: 0x... (嵌套表) print("Packed Table [4].key:", packedTable[4].key) -- 输出: Packed Table [4].key: value
  • 详解: table.pack 函数主要用于将可变数量的参数打包成一个表。这个表不仅包含了所有的参数值,还额外添加了一个字段 n 来记录参数的数量。这在处理变长参数列表或者需要将多个值作为一个整体传递时非常有用。

3.7 table.unpack (list [, i [, j]])

  • 功能: 解包列表 list 中索引从 ij 的元素,作为多个返回值返回。如果省略 i,则默认为 1。如果省略 j,则默认解包到列表的长度。

  • 参数:

    • list: 要解包的列表 (必须是一个表)。

    • i (可选): 起始索引,默认为 1。

    • j (可选): 结束索引,默认为列表的长度。

  • 返回值: 解包后的多个值。返回值的数量取决于解包的元素范围。

  • 实践示例:

local packedTable = table.pack(10, "hello", true) -- 解包所有元素 local val1, val2, val3 = table.unpack(packedTable) print("Unpacked values:", val1, val2, val3) -- 输出: Unpacked values: 10 hello true -- 解包索引 2 到 3 的元素 local val4, val5 = table.unpack(packedTable, 2, 3) print("Unpacked values (index 2-3):", val4, val5) -- 输出: Unpacked values (index 2-3): hello true local numberList = {1, 2, 3, 4, 5} -- 解包整个 numberList local n1, n2, n3, n4, n5 = table.unpack(numberList) print("Unpacked number list:", n1, n2, n3, n4, n5) -- 输出: Unpacked number list: 1 2 3 4 5
  • 详解: table.unpack 函数是 table.pack 的逆操作。它将一个表中的元素解包成多个返回值,可以方便地将表中的数据传递给需要多个参数的函数或者进行多重赋值。table.unpack 常用于函数调用时将表的元素作为参数列表展开。

3.8 table.find (list, value [, init]) (Lua 5.4 新增)

  • 功能: 在列表 list 中查找值 value,并返回其索引。如果找到多个匹配项,则返回第一个匹配项的索引。如果未找到,则返回 nil。可以指定可选的起始索引 init 进行搜索。

  • 参数:

    • list: 要搜索的列表 (必须是一个表)。

    • value: 要查找的值。

    • init (可选): 起始搜索索引,默认为 1。

  • 返回值: 如果找到 value,则返回其索引 (数字),否则返回 nil

  • 实践示例:

local searchTable = {"apple", "banana", "cherry", "banana", "date"} -- 查找 "banana" (默认从索引 1 开始) local index1 = table.find(searchTable, "banana") print("Index of 'banana' (first occurrence):", index1) -- 输出: Index of 'banana' (first occurrence): 2 -- 查找 "banana" (从索引 3 开始) local index2 = table.find(searchTable, "banana", 3) print("Index of 'banana' (starting from index 3):", index2) -- 输出: Index of 'banana' (starting from index 3): 4 -- 查找 "grape" (不存在) local index3 = table.find(searchTable, "grape") print("Index of 'grape':", index3) -- 输出: Index of 'grape': nil
  • 详解: table.find 函数是 Lua 5.4 版本新增的函数,用于在数字索引的列表中查找特定值。它提供了比手动循环遍历更简洁和高效的查找方式。可以指定起始索引,方便在列表中特定部分进行搜索。

3.9 迭代器函数: table.pairstable.ipairs

虽然 table 库本身没有直接提供迭代函数,但 Lua 提供了用于表迭代的内置函数 pairsipairs,它们经常与表库函数一起使用,因此在这里也简要介绍一下。

  • pairs (t): 返回一个迭代器函数,用于遍历表 t 的所有键值对。遍历顺序是不确定的 (不保证顺序)。适用于遍历关联数组类型的表。

  • ipairs (t): 返回一个迭代器函数,用于遍历表 t 的数字索引键值对,从索引 1 开始,直到遇到第一个非整数索引或索引为空的键。遍历顺序是按照数字索引升序排列的。适用于遍历数字索引数组类型的表。

实践示例:

local mixedTable = { [1] = "one", [2] = "two", name = "Lua", version = "5.4", [3] = "three" } print("Iterating with pairs:") for key, value in pairs(mixedTable) do print(key, value) end -- 输出 (顺序不确定,但会遍历所有键值对,包括数字索引和字符串索引): -- 1 one -- 2 two -- 3 three -- version 5.4 -- name Lua print("\nIterating with ipairs:") for index, value in ipairs(mixedTable) do print(index, value) end -- 输出 (只遍历数字索引键值对,且顺序是 1, 2, 3): -- 1 one -- 2 two -- 3 three
  • 详解: pairsipairs 是 Lua 中用于迭代表的关键函数。选择使用哪个迭代器取决于表的类型和遍历需求。

    • 如果需要遍历表的所有键值对 (包括非数字索引),使用 pairs

    • 如果只需要遍历数字索引数组类型的表,并且需要按照索引顺序遍历,使用 ipairs

4. 不推荐使用的表库函数 (Lua 5.1 及其之前版本)

在 Lua 5.1 及其之前的版本中,表库还包含 table.getntable.setn 函数,用于获取和设置表的 "数组" 部分的长度。但是,这些函数在 Lua 5.2 版本中已经被废弃,不建议使用。Lua 现在会自动跟踪表的数组部分长度,无需手动管理。

另外,table.foreachtable.foreachi 函数在 Lua 5.1 中被废弃,并被 pairsipairs 取代,后者提供了更通用和高效的迭代方式。

5. 表库实践技巧与最佳实践

  • 选择合适的函数: 根据具体需求选择最合适的表库函数。例如,插入元素使用 table.insert,删除元素使用 table.remove,连接字符串使用 table.concat,排序使用 table.sort 等。

  • 理解函数参数和返回值: 仔细阅读函数文档,理解每个函数的参数含义、可选参数以及返回值。

  • 注意就地修改: table.inserttable.removetable.sort 等函数会直接修改传入的表,而不是返回一个新的表。在某些情况下,可能需要先复制表再进行操作,以避免修改原始数据。

  • 性能考虑: 频繁的 table.inserttable.remove 操作,尤其是在表头部进行插入和删除,可能会导致性能下降,因为需要移动大量元素。在性能敏感的场景中,可以考虑使用其他数据结构或者优化表操作方式。table.concat 效率较高,适合处理大量字符串连接。table.sort 的性能取决于排序算法和比较函数的复杂度。

  • 迭代器的选择: 根据表的类型和遍历需求选择 pairsipairs 迭代器。对于数字索引数组,优先使用 ipairs 以获得更高效和有序的遍历。

  • 自定义比较函数: 充分利用 table.sort 的自定义比较函数功能,实现灵活的排序逻辑,满足各种复杂的排序需求.

6. 总结

Lua 表库是 Lua 语言中不可或缺的一部分,它为表这种核心数据结构提供了强大的操作支持。掌握表库的各种函数,能够让你更高效、更灵活地处理 Lua 中的数据,编写出更简洁、更强大的 Lua 代码。通过本文的详细讲解和实践示例,相信你已经对 Lua 表库有了更深入的理解,并能够在实际项目中灵活运用。 熟练掌握表库,是成为一名优秀的 Lua 程序员的关键一步。


发布者: 作者: 转发
评论区 (0)
U