lua知识点

一、前言

平常开发中C#用得比较多,lua没有重度使用过。这里主要记录一下lua的知识点,供自己回顾

二、知识点

2.1 数据类型

  • nil:表示无效值或者没有值,它是 Lua 的一个关键字(将nil赋予一个变量相当于删除它)
  • boolean:表示布尔类型,可以是 true 或 false
  • number:表示数字类型,包括整数和浮点数
  • string:表示字符串类型,可以是单引号或双引号包裹的文本
  • table:表示 Lua 中最重要的数据结构,由键值对组成
  • function:表示函数类型,可以是内置函数或自定义函数
  • userdata:表示 Lua 支持的扩展类型,通常用于表示 C 语言的数据结构
  • thread:表示 Lua 的协程类型,可以理解为独立的执行流程。

在lua中,boolean,number,string相当于C#里的值类型(C# 中,string 是引用类型),table则相当于引用类型

2.2 table表

2.2.1 table的构造

-- 初始化表
tab = {}

-- 指定值
tab[1]= "Lua"

-- 移除引用
tab = nil

2.2.2 table的结构

table可以分为哈希表和数组2部分,哈希表表示带有键值对的部分(类似C#的字典),数组则是没有键值对的部分。我们看看下面的代码分析

tab1 = { "apple","pear",  [1] = 1, [2] = 2, nil, "watermelon"}

--1.结构如下:
-- 哈希表部分:[1] = 1, [2] = 2
-- 数组部分:"apple","pear","nil","watermelon"

--2.接着tab会把数组部分,从左到右,从1开始自增附上key转为哈希表
--数组部分转化后为:[1] = apple, [2] = "pear",[3] = nil, [4] = "watermelon"

--3.我们可以看到数组部分和哈希表部分的key会冲突,table会以后面的数组部分为准。
--则转化为table结构如下:
--tab1 = {[1] = apple, [2] = "pear",[3] = nil, [4] = "watermelon"} 

2.3 ipairs和pairs的异同点

2.3.1 相同点

  • ipairs和pairs都可以用来对table进行遍历

2.3.2 不同点

  • ipairs:ipairs是有序遍历,从键[1]开始往上遍历,遇到nil或者键值断序了,就会停止遍历
  • pairs: pairs遍历顺序是不确定的,但会遍历所有键,遇到nil会跳过,继续遍历下一个key。

好,我们看下代码和打印

tab1 = { "apple", "pear", [1] = 1, [2] = 2, nil, "watermelon" }

print("---ipairs---")
for k, v in ipairs(tab1) do
    print(k, v)
end

print("---pairs---")

for k, v in pairs(tab1) do
    print(k, v)
end

输出如下:

根据2.2.2的分析,我们知道tab1最终结构为:
tab1 = {[1] = apple, [2] = "pear",[3] = nil, [4] = "watermelon"} 

---ipairs---
1	apple
2	pear
---pairs---
1	apple
2	pear
4	watermelon

输出结果是符合ipairs和pairs的遍历规则的

2.3 元表

元表也是一个表,我们可以为一个表设置元表

  • 设置元表
local tab = {}
local metaTab = {1,2}
-- 把表metaTab设置为表tab的元表
setmetatable(tab,metaTab);
  • 获取元表
-- 获取tab的元表
local targetMeta = getmetatable(tab)

2.4 元方法

2.4.1 定义

在 Lua 中,元方法(metamethod)是一种特殊的函数,它们被用来定义表的行为,元方法被存储在一个独立的metatable元表中。例如,在表中使用 + 运算符时,Lua 会查找其元表里是否有 __add 元方法,如果有,则使用该元方法实现表的加法操作。

2.4.2 常见的元方法

  • __index:用于查询表中不存在的键,当表中不存在指定键时,Lua 会自动调用 __index 元方法来返回一个默认值或者一个新的值。

  • __newindex:用于添加新的键值对,当尝试向表中添加一个不存在的键时,Lua 会自动调用 __newindex 元方法来添加新的键值对或者修改已有的键值对

  • __add、__sub、__mul、__div、__mod、__pow、__unm:用于重载运算符,当使用表参与数学运算时,Lua 会自动调用相应的元方法来实现运算。

  • __tostring:用于将表转换为字符串,当使用 tostring 函数将表转换为字符串时,Lua 会自动调用 __tostring 元方法来实现转换。

通过合理地使用元方法,我们可以为 table 定义各种不同的行为,从而实现更加灵活、高效、易用的数据结构和算法。

2.5 元表和元方法的应用

2.5.1 __index应用

我们可以使用__index元方法来为一个表增加键值对,如

local tab = {}
local metaTab = {1,2}
-- 把表metaTab设置为表tab的元表
setmetatable(tab,metaTab);
metaTab.__index = metaTab;

print(tab[1])

--输出
1
  • 表的key查找规则
    我们可以看到tab表是没有【1】这个key的,但是输出了1,这里就跟表的key查找规则有关了。查找规则如下:
  1. 比如tab[1],先查找tab里有没【1】这个key,有则输出,无则进入下一步
  2. 查看该表有无元表和__index元方法,没有的话返回nil,有的话,如果__index元方法指向一个方法,则根据该方法返回值,如果指向一个表,则进入下一步
  3. 查看该表有没【1】这个key,有则输出,无进看看该表有没有自己的元表和__index元方法(相当于又回到了第1步,直到找到或找不到为止)

因为tab没有【1】这个key,但是它有metaTab 元表和__index元方法,__index元方法指向了metaTab 元表本身,metaTab[1] = 1,所以返回了1

2.5.2 实现面向对象

我们可以使用元表和__index元方法来实现C#中类和对象的用法

-- 定义一个ClassA类
ClassA = {x = 1, y = 2}
ClassA.__index = ClassA

--定义一个New构造方法,返回对象实例
function ClassA.New(x,y)
    --创建新对象
    local o = {}
    --设置元表位ClassA
    setmetatable(o,ClassA);
    --为变量赋值
    o.x = x;
    o.y = y;
    return o;
end

--创建对象实例inst1
local inst1 = ClassA.New(3,4)
--创建对象实例inst2
local inst2 = ClassA.New(5)

--打印inst1
print("inst1.x:"..inst1.x)
print("inst1.y:"..inst1.y)

--打印inst2
print("inst2.x:"..inst2.x)
print("inst2.y:"..inst2.y)

输出如下

inst1.x:3
inst1.y:4
inst2.x:5
inst2.y:2

可以看到实现了类似C#创建对象的效果

2.5.3 __newindex应用

__newIndex 当尝试向表中添加一个不存在的键时,Lua 会自动调用 __newindex 元方法来添加新的键值对或者修改已有的键值对

我们可以用上面的例子加上__newindex方法后,来看下它的输出

ClassA = {x = 1, y = 2}
ClassA.__index = ClassA
ClassA.__newindex = ClassA

function ClassA.New(x,y)
    local o = {}
    setmetatable(o,ClassA);
    o.x = x;
    o.y = y;
    return o;
end
--创建2个实例,并赋值
local inst1 = ClassA.New(3,4)
local inst2 = ClassA.New(5,6)

--继续赋值
inst1.z = 7;
inst2.z = 8;

--打印inst1
print("inst1.x:"..inst1.x)
print("inst1.y:"..inst1.y)
print("inst1.z:"..inst1.z)

--打印inst2
print("inst2.x:"..inst2.x)
print("inst2.y:"..inst2.y)
print("inst2.z:"..inst2.z)

输出

inst1.x:5
inst1.y:6
inst1.z:8
inst2.x:5
inst2.y:6
inst2.z:8

可以看到inst1和inst2的输出都一样,因为有了__newindex元方法,当对表自身没有的key进行赋值,就会赋值到__newindex元方法对应的表。因为修改到的是同一个表,所以inst1和inst2的输出是一样的。

2.6 修饰方法时,. 和 : 的区别

. 和 **:**都可以用来修饰方法,用 : 修复方法时用自动传入类self变量,如

  --下面这2个方法等价
  function  class.func1(self)  end
  function  class:func1( )  end

可以看看下面的例子

ClassA = { x = 1, y = 2 }
ClassA.__index = ClassA
ClassA.__newindex = ClassA

function ClassA:func1(a)
    print("ClassA:func1:"..a)
    print("ClassA:func1+:"..(a + self.x))
end

function ClassA.func2(a)
    print("ClassA.func1:"..a)
    print("ClassA.func1+:"..(a + ClassA.x))
end

ClassA:func1(3)
ClassA.func2(3)

输出

ClassA:func1:3
ClassA:func1+:4
ClassA.func1:3
ClassA.func1+:4

猜你喜欢

转载自blog.csdn.net/aaa27987/article/details/131308076