【lua】lua学习笔记
Lua安装
- 下载lua环境
- 将文件解压到任意路径下
- 将2中的路径设置为电脑的全局变量
Lua数据类型
数据类型 | 描述 |
---|---|
nil | 无效值 |
boolean | false和true |
number | 双精度类型的实浮点数 |
string | 字符串类型,使用单引号或双引号表示 |
function | 由C或lua编写的函数 |
userdata | 表示任意存储在变量中的C的数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。 |
对table的索引使用方括号[]
外还可以使用.
1 | tlb = {"aa", "bb", "cc"} |
注:
在lua中只有nil表示false,0表示true
在lua中序列号从1开始
Lua变量
Lua 变量有三种类型:全局变量、局部变量、表中的域
Lua 中的变量全是全局变量,哪怕是语句块或是函数里,除非用 local 显式声明为局部变量
局部变量的作用域为从声明位置开始到所在语句块结束
变量的默认值均为 nil
可以和pytohn一样,赋值时可以一次赋值多个变量
1 | a, b = 10x, x*x |
常用作交换变量,或将函数调用返回给变量
1 | a, b = b, a |
Lua循环
while
循环
1 | a = 10 |
for
循环
数值for循环
1 | for var=exp1,exp2,exp3 do |
var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 **”执行体”**。exp3 是可选的,如果不指定,默认为1
1 | for i = 1, 10, 1 do |
泛型for循环
泛型for循环通过一个迭代器函数来遍历所有值,类似于C#中的foreach语句
1 | --打印数组a的所有值 |
拓展:
将上述的
ipairs
替换成pairs
是一样的结果,但他们的实现却有些不同ipairs适用于数组(i估计是integer的意思),pairs适用于对象,因为数组也是对象,所以pairs用于数组也没问题。
详细可看Lua的for in和pairs
repeat...until
循环
重复执行循环,直到指定的条件为真为止
1 | a = 10 |
Lua流程控制
1 | a = 11 |
注:Lua中的0为true
Lua函数
可变参数
1 | function add(...) |
注:可变参数直接传入值与传入表的用法不一样
select("#", ...)
可以用在表和值#...
只能用在表上
1 | -- 传入表 |
1 | -- 传入值 |
select()
函数
select("#", ...)
返回可变参数的长度select(n, ...)
用于返回从起点n开始到结束为止的所有参数列表a = select(n, ...)
将参数列表索引为n的参数赋值给a
1 | function f(...) |
遍历select(n, ...)
无法直接使用for循环直接遍历select(n, ...)
所返回的数据
需要获取到返回数据的长度,然后再通过索引号获取数据中的元素
1 | function f(...) |
table表
类似于python中的集合
索引从1开始
当我们获取 table 的长度的时候无论是使用
#
还是table.getn
其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。如tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5}
的长度为3。
表的常用方法:
方法 | 用途 |
---|---|
table.concat(table[,sep[,start[,end]]]) |
将表从start到end以sep分隔符隔开,使用时需注意 |
table.insert(table,[pos,]value) |
在pos位置插入元素,pos参数可选,默认尾部插入 |
table.remove(table[,pos]) |
移除元素,pos参数可选,默认尾部 元素 |
table.sort(table[,comp]) |
将表升序排序 |
注意:
在使用
table.concat
方法时,表需要有正确的格式才能正确显示。错误的格式:
1
2
3
4
5
6
7
8
9 -- 错误格式一:跳序号
tlb = {[1] = "aa", [2] = "bb", [3] = "cc", [10] = "dd"}
print(table.concat(tlb, ",")) -- 输出aa,bb,cc
-- 错误格式二:序号为非数字
tlb = {[1] = "aa", foo = "bb", [3] = "cc"}
print(table.concat(tlb, ",")) -- 输出aa
模块与包
创建一个模块就是创建一个table,将需要导出的常量、函数放入其中
创建模块
1 | -- 创建一个module.lua的文件 |
访问模块
1 | -- 创建一个test.lua的文件 |
Metatable元表
设置元表
1 | -- 方法一 |
返回元表
1 | getmetatable(mytable) |
__index元方法
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
1 | other = {foo = 3} |
__index可以包含一个函数,函数的参数固定为table和键
1 | tlb = setmetatable({key1 = "value1"}, { |
总结:
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
- 1.在表中查找,如果找到,返回该元素,找不到则继续
- 2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
- 3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。
__newindex元方法
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
1 | -- 当__newindex=table时 |
1 | -- 当__newindex=函数时,将table、键、值代入函数 |
拓展:
rawset(table, key, value)
方法:在不触发任何元方法的情况下将table[index]设为value(即不受__newindex的影响
rawget(table, index)
方法:同上,在不触发任何元方法的情况下获取table[index](即不受__index的影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14 local tableA = {}
local tableB = {NUM = 100}
local tableC = {}
setmetatable(tableA, {__index = tableB, __newindex = tableC})
print(tableA.NUM) -- 输出100
print(rawget(tableA,"NUM")) -- 输出nil
tableA.NAME = "AA"
print(tableA,NAME) -- 输出nil
print(tableC.NAME) -- AA
rawset(tableA, "NAME", "I AM AA")
print(tableA.NAME) -- 输出I AM AA
表的操作符
类似于python的魔法方法
模式 | 描述 |
---|---|
__add | 对应的运算符’+’ |
__sub | 对应的运算符 ‘-‘ |
__mul | 对应的运算符 ‘*’ |
__div | 对应的运算符 ‘/‘ |
__mod | 对应的运算符 ‘%’ |
__unm | 对应的运算符 ‘-‘ |
__concat | 对应的运算符 ‘..’ |
__eq | 对应的运算符 ‘==’ |
__lt | 对应的运算符 ‘<’ |
__le | 对应的运算符 ‘<=’ |
定义表的相加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 -- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大键值函数 table_maxn,即返回表最大键值
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1,newtable[i])
end
return mytable
end
})
secondtable = {4,5,6}
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
__tostring元方法
__tostring元方法用于修改表的输出行为
1 | mytable = setmetatable({ 10, 20, 30 }, { |
协程
方法 | 描述 |
---|---|
coroutine.create() | 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.status() | 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序 |
coroutine.wrap() | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 coroutine 的线程号 |
1 | function foo() |
以上实例中,我们定义了一个名为 foo 的函数作为协同程序。在函数中,我们使用 coroutine.yield 暂停了协同程序的执行,并返回了一个值
在主程序中,我们使用 coroutine.create 创建了一个协同程序对象,并使用 coroutine.resume 启动了它的执行。
在第一次调用 coroutine.resume 后,协同程序执行到 coroutine.yield 处暂停,并将值返回给主程序。然后,我们再次调用 coroutine.resume,并传入一个值作为协同程序恢复执行时的参数。
执行以上代码输出结果为:
1 | 协同程序 foo 开始执行 |
local value = coroutine.yield("暂停 foo 的执行")
的作用:
挂起协程,时协程暂停
将
"暂停 foo 的执行"
返回给启动这次协程的coroutine.resume
再次启动协程时获取参数赋值给value