10.表

10.表


10.1 知识点

基本概念

  • 表是 Lua 里最核心的复合数据结构。
  • 数组、二维数组、字典、简单的类和结构体,都可以用表来组织。
  • 表还提供了一些常用方法给我们使用。
  • 表直接赋值只是复制引用,不会自动复制整张表。
a = { name = "韬哥" }
b = a
b.name = "韬老师"

print(a.name) -- 韬老师
print(b.name) -- 韬老师

上面这个例子里,ab 指向的是同一张表。这个点后面写配置表、角色数据、临时数据时很容易踩坑。这里先知道有这个现象即可,深拷贝后面放到进阶篇单独整理。

一维数组

一维数组声明

  • 一维数组声明语法:表变量 = { 元素1, 元素2, …, 元素n }
  • 表的元素可以是任意类型。
a = { 1, 2, 3, "4", nil, "6", true, nil }

获得一维数组中的元素

  • 获得一维数组中的元素语法:表变量[索引]
  • Lua 中表的索引默认从 1 开始。
print(a[0]) -- nil
print(a[1]) -- 1
print(a[2]) -- 2
print(a[3]) -- 3
print(a[4]) -- 4
print(a[5]) -- nil
print(a[6]) -- "6"
print(a[7]) -- true
print(a[8]) -- nil

获取一维数组的长度

  • 获取一维数组的长度语法:#表变量
  • # 是获取长度的关键字。
  • 如果数组中间有 nil,也就是出现了“空洞”,# 返回的结果可能和直觉不一致。
  • 所以数组内容不连续时,不要太依赖 # 得到的长度。
  • 实际项目里,连续数组用 # 问题不大;如果是字典、稀疏表、有空洞的数组,就要谨慎。
a = { 1, 2, 3, "4", nil, "6", true, nil }
print(#a) -- 7(不同版本或不同构造方式下,空洞表的结果可能不稳定)

a = { 1, nil, 3, "4", nil }
print(#a) -- 1

a = { 1, 2, nil, 4, "5", true, nil }
print(#a) -- 2

遍历一维数组

-- 使用 for 循环遍历时,# 表变量获得表长的问题仍然存在
-- 连续数组可以这样写
-- 如果中间有 nil,就要注意遍历结果可能不完整
a = { 1, 2, 3, "4", nil, "6", true, nil }
for i = 1, #a do
    print(a[i])
end

二维数组

二维数组声明

-- 二维数组声明语法:
-- 表变量 = 
-- {
--  { 表1元素1, 表1元素2, 表1元素3 }, 
--  { 表2元素1, 表2元素2, 表2元素3 },
--   ..., 
--  { 表n元素1, 表n元素2, 表n元素3 }  
-- }

a = {
    { 1, 2, 3 },
    { 4, 5, 6 }
}

获得二维数组中的元素

-- 获得二维数组中的元素语法:表变量[索引1][索引2]
print(a[1][1]) -- 1
print(a[1][2]) -- 2
print(a[1][3]) -- 3
print(a[2][1]) -- 4
print(a[2][2]) -- 5
print(a[2][3]) -- 6

遍历二维数组

-- 使用两次 for 循环遍历
-- # 表变量获得表长的问题仍然存在,不赘述
for i = 1, #a do
    b = a[i]
    for j = 1, #b do
        print(b[j])
    end
end

自定义索引

  • 表自定义索引声明语法:表变量 = { [索引1] = 元素1, [索引2] = 元素2, …, [索引n] = 元素n }
  • 表自定义索引规则:指定索引的元素关联指定索引,未指定索引的元素会从 1 开始按顺序增加。
  • 注意:
  • 重复索引不会报错,同一个索引(key)后面的值会覆盖前面的值。
  • 自定义索引会让表更像字典,不要再完全按连续数组去理解它。
  • 同时 #表变量 返回表长也可能不准确,不要盲信 #表变量 得到的表长。
a = { [0] = 1, 2, 3, [-1] = 4, 5 }
print(#a)    -- 3,只把 2、3、5 算入表长
print(a[0])  -- 1
print(a[1])  -- 2
print(a[2])  -- 3
print(a[-1]) -- 4
print(a[3])  -- 5

-- 虽然 5 和 3 中间夹了个 -1 索引
-- 但是 5 还是被当成上一个没有自定义索引元素的下一个顺序索引
-- 比如上一个没有自定义索引的元素 3 是索引 2,所以 5 是索引 3
print(a[4]) -- nil,没有索引 4 的元素

a = { [1] = 1, [2] = 2, [4] = 4, [5] = 5 }
print(#a)       -- 5,2 和 4 中间只隔了一个 nil,结果可能把 5 也算进去
for i = 1, #a do
    print(a[i]) -- 1 2 nil 4 5
end

a = { [1] = 1, [2] = 2, [5] = 4, [6] = 6 }
print(#a)       -- 2,2 和 5 中间隔了两个 nil,只把 1、2 算进去
for i = 1, #a do
    print(a[i]) -- 1 2
end

迭代器遍历

  • #表变量 得到的表长不一定准确,尤其是有空洞或者自定义索引时。
  • 连续数组可以用 for i = 1, #a do
  • 有空洞的数组、字典、自定义索引表,一般用迭代器遍历更稳。
  • nextpairs 底层依赖的基础遍历函数。基础篇先会用 pairs 即可,不需要手写 next

ipairs遍历

  • ipairs 可以遍历键值对,也可以只遍历键不遍历值。
  • ipairs 从索引 1 开始往后遍历,小于等于索引 0 的元素遍历不到。
  • ipairs 只适合连续数组,索引中间断了,后面的内容就遍历不到。
  • 比如如下遍历找不到 5 索引所在的元素 6,因为没有 4 元素。
  • 可以看出,ipairs 仍然没有解决空洞数组的问题。
a = { [0] = 1, 2, [-1] = 3, 4, 5, [5] = 6 }

-- ipairs 迭代器遍历键值对
-- ipairs 遍历键值对语法:
-- for 键变量, 值变量 in ipairs(表变量) do
--     循环体
-- end

for i, k in ipairs(a) do
    print("ipairs遍历键值对" .. i .. "_" .. k)
    -- ipairs遍历键值对1_2
    -- ipairs遍历键值对2_4
    -- ipairs遍历键值对3_5
end

-- ipairs 迭代器遍历键
-- ipairs 遍历键语法:
-- for 键变量 in ipairs(表变量) do
--     循环体
-- end

for i in ipairs(a) do
    print("ipairs遍历键" .. i)
    -- ipairs遍历键1
    -- ipairs遍历键2
    -- ipairs遍历键3
end

pairs遍历

  • pairs 可以遍历键值对,也可以只遍历键不遍历值。
  • pairs 能够把所有键都遍历出来,可以得到所有键值对。
  • pairs 能解决 #表变量 在自定义索引、字典、空洞表上的一些问题。
  • pairs 的遍历顺序不保证。不要写依赖 pairs 顺序的业务逻辑。
a = { [0] = 1, 2, [-1] = 3, 4, 5, [5] = 6 }

-- pairs 遍历键值对语法:
-- for 键变量, 值变量 in pairs(表变量) do
--     循环体
-- end
-- pairs 能够把所有键都找到,这样就可以得到所有键值对

for i, v in pairs(a) do
    print("pairs遍历键值对" .. i .. "_" .. v)
    -- pairs遍历键值对1_2
    -- pairs遍历键值对2_4
    -- pairs遍历键值对3_5
    -- pairs遍历键值对0_1
    -- pairs遍历键值对-1_3
    -- pairs遍历键值对5_6
end

-- pairs 遍历键语法:
-- for 键变量 in pairs(表变量) do
--     循环体
-- end

for i in pairs(a) do
    print("pairs遍历键" .. i)
    -- pairs遍历键1
    -- pairs遍历键2
    -- pairs遍历键3
    -- pairs遍历键0
    -- pairs遍历键-1
    -- pairs遍历键5
end

字典

字典的声明

-- 字典的声明语法:表变量 = { ["键1"] = "值1", ["键2"] = "值2", ..., ["键3"] = "值3" }
-- 字典是由键值对构成,和自定义索引声明类似,只是键和值多了双引号 "" 包裹
a = { ["name"] = "韬哥", ["age"] = 22, ["1"] = 1 }

访问字典值

-- 表变量["键"] 用中括号来访问值,键要带上双引号 ""
print(a["name"]) -- 韬哥
print(a["age"])  -- 22
print(a["1"])    -- 1

-- 表变量.键 用点访问值,类似 C# 中 .成员变量 的形式得到值
print(a.name)    -- 韬哥
print(a.age)     -- 22

-- 虽然可以通过 .成员变量 的形式得到值,但是不能是数字
-- print(a.1) -- 报错
print(a["1"]) -- 1

修改字典值

-- 表变量["键"] = "新值"
a["name"] = "韬老师"
print(a["name"]) -- 韬老师

-- 表变量.键 = "新值"
a.name = "韬老师帅"
print(a.name) -- 韬老师帅

新增字典值

-- 表变量["新键"] = "新值"
a["sex"] = false
print(a["sex"]) -- false

-- 表变量.新键 = "新值"
a.handsome = true
print(a.handsome) -- true

删除字典值

-- 表变量["键"] = nil
a["sex"] = nil
print(a["sex"]) -- nil

-- 表变量.键 = nil
a.handsome = nil
print(a.handsome) -- nil

字典的遍历

-- 遍历字典一般用 pairs 迭代器
-- 键值对遍历

for k, v in pairs(a) do
    -- 可以传多个参数,一样可以打印出来
    print(k, v)
    -- 1	1
    -- name	韬老师帅
    -- age	22
end

-- 遍历键,通过键得到值
for k in pairs(a) do
    print(k)
    print(a[k])
    -- 1
    -- 1
    -- name
    -- 韬老师帅
    -- age
    -- 22
end

-- 遍历值,可以用 _ 省略键
for _, v in pairs(a) do
    print(_, v)
    -- 1	1
    -- name	韬老师帅
    -- age	22
end

类和结构体

  • Lua 中默认没有 C# 那种面向对象语法,需要自己用表来模拟。
  • Lua 中实现简单的类和结构体,底层思路都离不开表。

类声明

-- 类声明语法:
-- 类 = {
--     成员变量,
--     成员函数,
--     ...
-- }

Student = {
    -- 年龄
    age = 1,

    -- 性别
    sex = true,

    -- 打印函数1
    Print1 = function()
        print("进入打印函数1")

        print(age)
        -- 这样打印的 age 和 Student 类中的 age 没有任何关系
        -- 打印的 age 是一个全局变量

        -- 在表内部函数中,调用类本身的属性或者方法,要指定类
        -- 类.属性 或 类.方法()
        print(Student.age)
        print(Student.sex)

        print("退出打印函数1")
    end,

    -- 打印函数2
    Print2 = function(myStudent)
        print("进入打印函数2")

        -- 可以使用有参函数,把当前类作为一个参数传进来
        -- 在函数内部通过参数访问当前类
        print(Student.age)
        print(myStudent.sex)

        print("退出打印函数2")
    end
}

实例化类对象

  • C# 是使用类 new 出对象,通过对象访问成员。
  • Lua 中默认没有 new 的概念,表更像是一个装着很多字段和函数的容器。
  • 真正完整的面向对象写法后面在面向对象篇再整理。

得到类的成员变量

-- 直接 类.变量 得到变量
print(Student.age) -- 1
print(Student.sex) -- true

在类外添加的成员变量

-- 声明类过后,可以直接在类外 类.变量 = 值 添加成员变量
Student.name = "韬老狮"
print(Student.name) -- 韬老狮

使用.调用类的成员方法

Student.Print1()
-- 进入打印函数1
-- nil
-- 1
-- true
-- 退出打印函数1

Student.Print2(Student) -- 传入当前类作为参数
-- 进入打印函数2
-- 1
-- true
-- 退出打印函数2

使用.在类外添加的成员方法

-- 声明类过后,可以直接在类外
-- 类.方法 = function()
--     函数体
-- end
-- 来添加成员方法

Student.Print3 = function()
    print("进入打印函数3")

    print(Student.age)
    print(Student.sex)

    print("退出打印函数3")
end

Student.Print3()
-- 进入打印函数3
-- 1
-- true
-- 退出打印函数3

使用:调用类的成员方法

-- 冒号调用方法,会默认把调用者作为调用方法的第一个参数传入方法中
Student:Print2() -- 虽然没有显式传入 Student,但因为是 : 调用函数,已经隐式传入 Student
-- 进入打印函数2
-- 1
-- true
-- 退出打印函数2

使用:在类外添加的成员方法 self来表示默认传入的第一个参数

-- 声明类过后,可以直接在类外
-- 类:方法 = function()
--     函数体
-- end
-- 来添加成员方法
-- 使用了 : 声明方法后,代表方法会有一个默认参数
-- 在方法内,通过 self 表示默认传入的第一个参数

function Student:Print4() -- 虽然没有显式参数,但是因为是 : 声明函数,已经隐式让这个函数有参数了
    print("进入打印函数4")

    print(self.age)
    print(self.sex)

    print("退出打印函数4")
end

在类外调用使用:在类外添加的成员方法

-- Student.Print4() -- 报错,Print4 因为是 : 声明函数,需要传入参数

Student.Print4(Student) -- 传入了 Student 类自身作为参数,正常调用
-- 进入打印函数4
-- 1
-- true
-- 退出打印函数4

Student:Print4() -- 冒号调用方法,会把调用者 Student 作为第一个参数传进去,正常调用
-- 进入打印函数4
-- 1
-- true
-- 退出打印函数4

表的公共方法

插入 table.insert

t1 = { { age = 1, name = "111" }, { age = 2, name = "222" } }
t2 = { name = "韬老狮", sex = true }

-- table.insert(要插入元素的表, 插入的元素)
-- 这里会把 t2 作为 t1 顺序索引的最后一个元素
print(#t1)        -- 2
table.insert(t1, t2)
print(#t1)        -- 3
print(t1[1].name) -- 111
print(t1[2].name) -- 222
print(t1[3].name) -- 韬老狮,代表把 t2 插入成功了

删除 table.remove

t1 = { { age = 1, name = "111" }, { age = 2, name = "222" }, { name = "韬老狮", sex = true } }

-- table.remove(要移除元素的表) 会移除传入表最后一个索引的内容
print(#t1)        -- 3
table.remove(t1)
print(#t1)        -- 2
print(t1[1].name) -- 111
print(t1[2].name) -- 222
print(t1[3])      -- nil,最后一个元素被移除了

-- table.remove(要移除元素的表, 移除元素索引)
print(#t1)        -- 2
table.remove(t1, 1)
print(#t1)        -- 1
print(t1[1].name) -- 222,name 为 111 的元素被移除了

排序 table.sort

t1 = { 5, 2, 4, 3, 1 }

-- table.sort(要排序的表) 默认升序排列
table.sort(t1)

for _, v in pairs(t1) do
    print(v)
    -- 1
    -- 2
    -- 3
    -- 4
    -- 5
end

-- table.sort(要排序的表, 排序规则函数)
-- 排序函数接受两个参数,通常命名为 a 和 b,用于表示待比较的两个元素。
-- 排序函数根据 a 和 b 的比较结果返回 true 或 false,用来决定元素的相对顺序。
-- 如果排序函数返回 true,表示 a 应该排在 b 前面。
-- 如果排序函数返回 false,表示 b 应该排在 a 前面。
-- 排序函数必须返回布尔值,且只有 true 或 false。

-- 降序
table.sort(t1, function(a, b)
    return a > b
end)

for _, v in pairs(t1) do
    print(v)
    -- 5
    -- 4
    -- 3
    -- 2
    -- 1
end

-- 升序
function AscendingSort(a, b)
    return a < b
end

table.sort(t1, AscendingSort)

for _, v in pairs(t1) do
    print(v)
    -- 1
    -- 2
    -- 3
    -- 4
    -- 5
end

拼接 table.concat

t1 = { "123", "456", "789", "10101" }

-- table.concat(要拼接的表, "元素间的符号") 返回值是一个拼接好的字符串
str = table.concat(t1, "@@")
print(str) -- 123@@456@@789@@10101

10.2 知识点代码

Lesson10_表.lua

print("**********表************")

print("**********知识点一 基本概念************")
-- 表是 Lua 里最核心的复合数据结构
-- 数组、二维数组、字典、简单的类和结构体,都可以用表来组织
-- 表还提供了一些常用方法给我们使用
-- 表直接赋值只是复制引用,不会自动复制整张表

a = { name = "韬哥" }
b = a
b.name = "韬老师"

print(a.name) -- 韬老师
print(b.name) -- 韬老师

print("**********知识点二 一维数组************")

-- 一维数组声明语法:表变量 = { 元素1, 元素2, ..., 元素n }
-- 表的元素可以是任意类型
a = { 1, 2, 3, "4", nil, "6", true, nil }

-- 获得一维数组中的元素语法:表变量[索引]
-- Lua 中表的索引默认从 1 开始
print(a[0]) -- nil
print(a[1]) -- 1
print(a[2]) -- 2
print(a[3]) -- 3
print(a[4]) -- 4
print(a[5]) -- nil
print(a[6]) -- "6"
print(a[7]) -- true
print(a[8]) -- nil

-- 获取一维数组的长度语法:#表变量
-- # 是获取长度的关键字
-- 如果数组中间有 nil,也就是出现了“空洞”,# 返回的结果可能和直觉不一致
-- 所以数组内容不连续时,不要太依赖 # 得到的长度
-- 实际项目里,连续数组用 # 问题不大
-- 如果是字典、稀疏表、有空洞的数组,就要谨慎
a = { 1, 2, 3, "4", nil, "6", true, nil }
print(#a) -- 7(不同版本或不同构造方式下,空洞表的结果可能不稳定)

a = { 1, nil, 3, "4", nil }
print(#a) -- 1

a = { 1, 2, nil, 4, "5", true, nil }
print(#a) -- 2

-- 遍历一维数组
-- 使用 for 循环遍历时,# 表变量获得表长的问题仍然存在
-- 连续数组可以这样写
-- 如果中间有 nil,就要注意遍历结果可能不完整
a = { 1, 2, 3, "4", nil, "6", true, nil }
for i = 1, #a do
    print(a[i])
end

print("**********知识点三 二维数组************")

-- 二维数组声明语法:
-- 表变量 = 
-- {
--  { 表1元素1, 表1元素2, 表1元素3 }, 
--  { 表2元素1, 表2元素2, 表2元素3 },
--   ..., 
--  { 表n元素1, 表n元素2, 表n元素3 }  
-- }

a = {
    { 1, 2, 3 },
    { 4, 5, 6 }
}

-- 获得二维数组中的元素语法:表变量[索引1][索引2]
print(a[1][1]) -- 1
print(a[1][2]) -- 2
print(a[1][3]) -- 3
print(a[2][1]) -- 4
print(a[2][2]) -- 5
print(a[2][3]) -- 6

-- 遍历二维数组
-- 使用两次 for 循环遍历
-- # 表变量获得表长的问题仍然存在,不赘述
for i = 1, #a do
    b = a[i]
    for j = 1, #b do
        print(b[j])
    end
end

print("**********知识点四 自定义索引************")

-- 表自定义索引声明语法:表变量 = { [索引1] = 元素1, [索引2] = 元素2, ..., [索引n] = 元素n }
-- 表自定义索引规则:指定索引的元素关联指定索引,未指定索引的元素会从 1 开始按顺序增加
-- 注意:
-- 重复索引不会报错,同一个索引(key)后面的值会覆盖前面的值
-- 自定义索引会让表更像字典,不要再完全按连续数组去理解它
-- 同时 #表变量 返回表长也可能不准确,不要盲信 #表变量 得到的表长

a = { [0] = 1, 2, 3, [-1] = 4, 5 }
print(#a)    -- 3,只把 2、3、5 算入表长
print(a[0])  -- 1
print(a[1])  -- 2
print(a[2])  -- 3
print(a[-1]) -- 4
print(a[3])  -- 5

-- 虽然 5 和 3 中间夹了个 -1 索引
-- 但是 5 还是被当成上一个没有自定义索引元素的下一个顺序索引
-- 比如上一个没有自定义索引的元素 3 是索引 2,所以 5 是索引 3
print(a[4]) -- nil,没有索引 4 的元素

a = { [1] = 1, [2] = 2, [4] = 4, [5] = 5 }
print(#a)       -- 5,2 和 4 中间只隔了一个 nil,结果可能把 5 也算进去
for i = 1, #a do
    print(a[i]) -- 1 2 nil 4 5
end

a = { [1] = 1, [2] = 2, [5] = 4, [6] = 6 }
print(#a)       -- 2,2 和 5 中间隔了两个 nil,只把 1、2 算进去
for i = 1, #a do
    print(a[i]) -- 1 2
end

print("**********知识点五 迭代器遍历************")

-- #表变量 得到的表长不一定准确,尤其是有空洞或者自定义索引时
-- 连续数组可以用 for i = 1, #a do
-- 有空洞的数组、字典、自定义索引表,一般用迭代器遍历更稳
-- next 是 pairs 底层依赖的基础遍历函数
-- 基础篇先会用 pairs 即可,不需要手写 next

-- ipairs 遍历
-- ipairs 可以遍历键值对,也可以只遍历键不遍历值
-- ipairs 从索引 1 开始往后遍历,小于等于索引 0 的元素遍历不到
-- ipairs 只适合连续数组,索引中间断了,后面的内容就遍历不到
-- 比如如下遍历找不到 5 索引所在的元素 6,因为没有 4 元素
-- 可以看出,ipairs 仍然没有解决空洞数组的问题

a = { [0] = 1, 2, [-1] = 3, 4, 5, [5] = 6 }

-- ipairs 迭代器遍历键值对
-- ipairs 遍历键值对语法:
-- for 键变量, 值变量 in ipairs(表变量) do
--     循环体
-- end

for i, k in ipairs(a) do
    print("ipairs遍历键值对" .. i .. "_" .. k)
    -- ipairs遍历键值对1_2
    -- ipairs遍历键值对2_4
    -- ipairs遍历键值对3_5
end

-- ipairs 迭代器遍历键
-- ipairs 遍历键语法:
-- for 键变量 in ipairs(表变量) do
--     循环体
-- end

for i in ipairs(a) do
    print("ipairs遍历键" .. i)
    -- ipairs遍历键1
    -- ipairs遍历键2
    -- ipairs遍历键3
end

-- pairs 遍历
-- pairs 可以遍历键值对,也可以只遍历键不遍历值
-- pairs 能够把所有键都遍历出来,可以得到所有键值对
-- pairs 能解决 #表变量 在自定义索引、字典、空洞表上的一些问题
-- 但 pairs 的遍历顺序不保证。不要写依赖 pairs 顺序的业务逻辑

a = { [0] = 1, 2, [-1] = 3, 4, 5, [5] = 6 }

-- pairs 遍历键值对语法:
-- for 键变量, 值变量 in pairs(表变量) do
--     循环体
-- end
-- pairs 能够把所有键都找到,这样就可以得到所有键值对

for i, v in pairs(a) do
    print("pairs遍历键值对" .. i .. "_" .. v)
    -- pairs遍历键值对1_2
    -- pairs遍历键值对2_4
    -- pairs遍历键值对3_5
    -- pairs遍历键值对0_1
    -- pairs遍历键值对-1_3
    -- pairs遍历键值对5_6
end

-- pairs 遍历键语法:
-- for 键变量 in pairs(表变量) do
--     循环体
-- end

for i in pairs(a) do
    print("pairs遍历键" .. i)
    -- pairs遍历键1
    -- pairs遍历键2
    -- pairs遍历键3
    -- pairs遍历键0
    -- pairs遍历键-1
    -- pairs遍历键5
end

print("**********知识点六 字典************")

-- 字典的声明
-- 字典的声明语法:表变量 = { ["键1"] = "值1", ["键2"] = "值2", ..., ["键3"] = "值3" }
-- 字典是由键值对构成,和自定义索引声明类似,只是键和值多了双引号 "" 包裹
a = { ["name"] = "韬哥", ["age"] = 22, ["1"] = 1 }

-- 访问字典值
-- 表变量["键"] 用中括号来访问值,键要带上双引号 ""
print(a["name"]) -- 韬哥
print(a["age"])  -- 22
print(a["1"])    -- 1

-- 表变量.键 用点访问值,类似 C# 中 .成员变量 的形式得到值
print(a.name)    -- 韬哥
print(a.age)     -- 22

-- 虽然可以通过 .成员变量 的形式得到值,但是不能是数字
-- print(a.1) -- 报错
print(a["1"]) -- 1

-- 修改字典值
-- 表变量["键"] = "新值"
a["name"] = "韬老师"
print(a["name"]) -- 韬老师

-- 表变量.键 = "新值"
a.name = "韬老师帅"
print(a.name) -- 韬老师帅

-- 新增字典值
-- 表变量["新键"] = "新值"
a["sex"] = false
print(a["sex"]) -- false

-- 表变量.新键 = "新值"
a.handsome = true
print(a.handsome) -- true

-- 删除字典值
-- 表变量["键"] = nil
a["sex"] = nil
print(a["sex"]) -- nil

-- 表变量.键 = nil
a.handsome = nil
print(a.handsome) -- nil

-- 字典的遍历
-- 遍历字典一般用 pairs 迭代器
-- 键值对遍历

for k, v in pairs(a) do
    -- 可以传多个参数,一样可以打印出来
    print(k, v)
    -- 1	1
    -- name	韬老师帅
    -- age	22
end

-- 遍历键,通过键得到值
for k in pairs(a) do
    print(k)
    print(a[k])
    -- 1
    -- 1
    -- name
    -- 韬老师帅
    -- age
    -- 22
end

-- 遍历值,可以用 _ 省略键
for _, v in pairs(a) do
    print(_, v)
    -- 1	1
    -- name	韬老师帅
    -- age	22
end

print("**********知识点七 类和结构体************")

-- Lua 中默认没有 C# 那种面向对象语法,需要自己用表来模拟
-- Lua 中实现简单的类和结构体,底层思路都离不开表

-- 类声明语法:
-- 类 = {
--     成员变量,
--     成员函数,
--     ...
-- }

Student = {
    -- 年龄
    age = 1,

    -- 性别
    sex = true,

    -- 打印函数1
    Print1 = function()
        print("进入打印函数1")

        print(age)
        -- 这样打印的 age 和 Student 类中的 age 没有任何关系
        -- 打印的 age 是一个全局变量

        -- 在表内部函数中,调用类本身的属性或者方法,要指定类
        -- 类.属性 或 类.方法()
        print(Student.age)
        print(Student.sex)

        print("退出打印函数1")
    end,

    -- 打印函数2
    Print2 = function(myStudent)
        print("进入打印函数2")

        -- 可以使用有参函数,把当前类作为一个参数传进来
        -- 在函数内部通过参数访问当前类
        print(Student.age)
        print(myStudent.sex)

        print("退出打印函数2")
    end
}

-- C# 是使用类 new 出对象,通过对象访问成员
-- Lua 中默认没有 new 的概念,表更像是一个装着很多字段和函数的容器
-- 真正完整的面向对象写法后面在面向对象篇再整理

-- 得到类的成员变量
-- 直接 类.变量 得到变量
print(Student.age) -- 1
print(Student.sex) -- true

-- 在类外添加的成员变量
-- 声明类过后,可以直接在类外 类.变量 = 值 添加成员变量
Student.name = "韬老狮"
print(Student.name) -- 韬老狮

-- 使用 . 调用类的成员方法
Student.Print1()
-- 进入打印函数1
-- nil
-- 1
-- true
-- 退出打印函数1

Student.Print2(Student) -- 传入当前类作为参数
-- 进入打印函数2
-- 1
-- true
-- 退出打印函数2

-- 使用 . 在类外添加的成员方法
-- 声明类过后,可以直接在类外
-- 类.方法 = function()
--     函数体
-- end
-- 来添加成员方法

Student.Print3 = function()
    print("进入打印函数3")

    print(Student.age)
    print(Student.sex)

    print("退出打印函数3")
end

Student.Print3()
-- 进入打印函数3
-- 1
-- true
-- 退出打印函数3

-- 使用 : 调用类的成员方法
-- 冒号调用方法,会默认把调用者作为调用方法的第一个参数传入方法中
Student:Print2() -- 虽然没有显式传入 Student,但因为是 : 调用函数,已经隐式传入 Student
-- 进入打印函数2
-- 1
-- true
-- 退出打印函数2

-- 使用 : 在类外添加的成员方法
-- 声明类过后,可以直接在类外
-- 类:方法 = function()
--     函数体
-- end
-- 来添加成员方法
-- 使用了 : 声明方法后,代表方法会有一个默认参数
-- 在方法内,通过 self 表示默认传入的第一个参数

function Student:Print4() -- 虽然没有显式参数,但是因为是 : 声明函数,已经隐式让这个函数有参数了
    print("进入打印函数4")

    print(self.age)
    print(self.sex)

    print("退出打印函数4")
end

-- Student.Print4() -- 报错,Print4 因为是 : 声明函数,需要传入参数

Student.Print4(Student) -- 传入了 Student 类自身作为参数,正常调用
-- 进入打印函数4
-- 1
-- true
-- 退出打印函数4

Student:Print4() -- 冒号调用方法,会把调用者 Student 作为第一个参数传进去,正常调用
-- 进入打印函数4
-- 1
-- true
-- 退出打印函数4

print("**********知识点八 表的公共方法************")

-- 插入
t1 = { { age = 1, name = "111" }, { age = 2, name = "222" } }
t2 = { name = "韬老狮", sex = true }

-- table.insert(要插入元素的表, 插入的元素)
-- 这里会把 t2 作为 t1 顺序索引的最后一个元素
print(#t1)        -- 2
table.insert(t1, t2)
print(#t1)        -- 3
print(t1[1].name) -- 111
print(t1[2].name) -- 222
print(t1[3].name) -- 韬老狮,代表把 t2 插入成功了

-- 删除
t1 = { { age = 1, name = "111" }, { age = 2, name = "222" }, { name = "韬老狮", sex = true } }

-- table.remove(要移除元素的表) 会移除传入表最后一个索引的内容
print(#t1)        -- 3
table.remove(t1)
print(#t1)        -- 2
print(t1[1].name) -- 111
print(t1[2].name) -- 222
print(t1[3])      -- nil,最后一个元素被移除了

-- table.remove(要移除元素的表, 移除元素索引)
print(#t1)        -- 2
table.remove(t1, 1)
print(#t1)        -- 1
print(t1[1].name) -- 222,name 为 111 的元素被移除了

-- 排序
t1 = { 5, 2, 4, 3, 1 }

-- table.sort(要排序的表) 默认升序排列
table.sort(t1)

for _, v in pairs(t1) do
    print(v)
    -- 1
    -- 2
    -- 3
    -- 4
    -- 5
end

-- table.sort(要排序的表, 排序规则函数)
-- 排序函数接受两个参数,通常命名为 a 和 b,用于表示待比较的两个元素
-- 排序函数根据 a 和 b 的比较结果返回 true 或 false,用来决定元素的相对顺序
-- 如果排序函数返回 true,表示 a 应该排在 b 前面
-- 如果排序函数返回 false,表示 b 应该排在 a 前面
-- 排序函数必须返回布尔值,且只有 true 或 false

-- 降序
table.sort(t1, function(a, b)
    return a > b
end)

for _, v in pairs(t1) do
    print(v)
    -- 5
    -- 4
    -- 3
    -- 2
    -- 1
end

-- 升序
function AscendingSort(a, b)
    return a < b
end

table.sort(t1, AscendingSort)

for _, v in pairs(t1) do
    print(v)
    -- 1
    -- 2
    -- 3
    -- 4
    -- 5
end

-- 拼接
t1 = { "123", "456", "789", "10101" }

-- table.concat(要拼接的表, "元素间的符号") 返回值是一个拼接好的字符串
str = table.concat(t1, "@@")
print(str) -- 123@@456@@789@@10101


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏