Lua学习笔记——基础(壹)
一、注释
-- 单行注释
--[[
多行注释1
]]
--[[
多行注释2
]]--
--[[
多行注释3
--]]
二、变量
1、变量类型:
nil、boolean、number、string、userdata、function、thread 、table

2、几个注意点:
① Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值 —— 自动识别类型,和 python 很像
② 通过 type 函数可以获取变量类型,该函数返回值是 string
③ Lua 中使用未赋值的变量不会报错,默认值是 nil
3、简单的四种变量类型:
nil、boolean、number、string
-- 1、nil 类似c#的null
a = nil
print(type(a)) -- nil
-- 2、number,所有数值都是number
a = 1
print(type(a)) -- number
a = 1.23
print(type(a)) -- number
-- 3、string,使用双引号或单引号包裹,在lua里没有char
a = "双引号包裹的字符串"
print(type(a)) -- string
a = '单引号包裹的字符串'
print(type(a)) -- string
-- 4、boolean,布尔类型,为true或false
a = true
print(type(a)) -- boolean
-- 5、使用未赋值的变量
print(b) -- nil
三、字符串操作
1、字符串操作:
-- 1、字符串长度
-- 一个英文字符占1个长度、一个汉字占3个长度
s = "abcdefG"
print(#s) -- 7
s = "abcdefG字符串"
print(#s) -- 16
-- 2、多行字符串(多行字符串前面加--便是多行注释)
s = [[这是多行字符串
我可以写很多行]]
print(s)
-- 3、字符串拼接
print("Hello" .. " World!") -- Hello World!
-- 4、格式化字符串(和C很像)
print(string.format("你好,我今年%d岁了",18)) -- 你好,我今年18岁了
-- 5、其他类型转字符串(显示转换)
a = true
print(tostring(a)) -- true
-- 6、小写转大写
str = "aBcdEFg"
print(string.upper(str)) -- ABCDEFG
-- 7、大写转小写
print(string.lower(str)) -- abcdefg
-- 8、字符串翻转
print(string.reverse(str)) -- gFEdcBa
-- 9、字符串索引查找(Lua中,索引从1开始)
print(string.find(str,"cdE")) -- 3 5
-- 10、截取字符串
print(string.sub(str,3,5)) -- cdE
-- 11、字符串重复,获取字符串的n个拷贝
print(string.rep(str,2)) -- aBcdEFgaBcdEFg
-- 12、字符串替换(返回值第二个参数代表替换次数)
print(string.gsub(str,"cd","**")) -- aB**EFg 1
-- 13、字符转ASCII码
a = string.byte("Lua",1) -- 将字符串第1个字符转成ASCII码
print(a) -- 76
-- 14、ASCII码转字符
print(string.char(a)) -- L
2、几个注意点:
① 使用#可以获取字符串长度以及表的长度(后面会学习)
② Lua 中,索引是从 1 开始算的,而不是 0
③ 使用..可以拼接字符串
四、运算符
1、算术运算符:
a = 2
b = 5
-- 加法
print(a+b) -- 7
-- 减法
print(a-b) -- -3
-- 乘法
print(a*b) -- 10
-- 除法
print(a/b) -- 0.4
-- 取余
print(a%b) -- 2
-- 幂运算
print(a^b) -- 32
-- 整除(Lua版本大于等于5.3)
-- 5 // 2
-- 注:如果是 string 和 number 进行算术运算符操作,则string会自动转成number
print("123.4" + 1) -- 124.4
2、条件运算符:
-- 大于
print(a>b) -- false
-- 小于
print(a=b) -- false
-- 小于等于
print(a<=b) -- true
-- 不等于(使用~符号,不是!)
print(a~=b) -- true
3、逻辑运算符:
-- 与
print(true and false) -- false
-- 或
print(true or false) -- true
-- 非
print(not false) -- true
4、其他运算符:
① .. 连接两个字符串
② # 一元运算符,返回字符串或表的长度
5、Lua 不支持的运算符:
① 不支持位运算如 & 和 |
② 不支持三目运算符如? :
③ Lua 没有自增自减 ++ –-,也没有复合运算符 += -= *= /= %=
6、几个注意点:
① 算术运算中,如果是 string 和 number 进行算术运算符操作,则 string 会自动转成 number 进行运算(可以转换的话)
② 条件运算符中,不等于使用~= 而不是!=
③ 逻辑运算符中,逻辑运算符支持 “短路” 原则
五、条件分支语句
1、语法:
-- 1、单分支
if true then
print("123") -- 123
end
-- 2、双分支
if false then
print("123")
else
print("456") -- 456
end
-- 3、多分支
if false then
print("123")
elseif true then
print("456") -- 456
else
print("789")
end
2、几个注意点:
① elseif 要连着写
② Lua 中没有 switch 和三目运算符
六、循环语句
1、语法:
-- 1、while循环语句
num = 0
while num < 5 do
print(num) -- 0 1 2 3 4
num = num + 1
end
-- 2、repeat循环语句(do while循环)
num = 0
repeat
print(num) -- 0 1 2 3 4 5
num = num + 1
until num > 5
-- 3、for循环语句
for i=2,5 do -- 默认自增1
print(i) -- 2 3 4 5
end
for i=1,5,2 do
print(i) -- 1 3 5
end
2、几个注意点:
① do…while 循环在 Lua 中是 repeat…until
② for 循环中 3 个参数分别代表起始值,目标值、步长,其中步长默认为1
七、函数
1、函数的写法:
-- 函数的两种写法
function 函数名(参数)
方法体
end
变量名 = function(参数)
方法体
end
2、几个函数示例:
Lua 的函数非常灵活,通过以下例子可以简单总结函数的几个规律:
(1)函数可接收任意个数量的参数:
如果实参数量多于形参,则多余的实参会被忽略,
如果实参数量少于形参,则缺少的参数会补为 nil
(2)可用多个变量接收函数的返回值:
如果函数内部没有返回值(没有 return),则默认返回 nil
如果外部接收的变量少于返回值,则多余的返回值会被忽略
如果外部接收的变量多于返回值,多余的变量被赋值为 nil
-- 1、无参无返回值函数
function F1()
print("F1函数")
end
F1() -- F1函数
F2 = function()
print("F2函数")
end
F2() -- F2函数
-- 2、有参无返回值函数
function F3(a)
print(a)
end
F3(1) -- 1
-- 几个特殊情况
F3() -- 不传参:参数默认为nil
b = F3(1) -- 接收返回值:返回值为nil
F3(1,2,3) -- 传多个参数:丢弃后面多余的参数,只接受第一个参数
-- 3、无参有返回值函数
function F4(a)
return a
end
temp = F4(5)
print(temp) -- 5
-- 可以有多个返回值,外面可用多个变量来接收,如果接收的变量数量不一致,则只接收对应位置的返回值
function F5(a)
return a,"123",true
end
temp,temp2,temp3 = F5(1) -- temp:1,temp2:"123",temp3:true
3、变长参数:
使用…即可代表不定长参数,使用变长参数时需要先用表存起来:
function Func(...)
arg = {...} -- 用表存起来
for i=1,#arg do
print(arg[i])
end
end
Func(1,2,3,4,5) -- 1 2 3 4 5
4、函数嵌套:
函数内部可以声明函数:
function Func()
return function()
print("你好,我是func!")
end
end
myfunc = Func()
myfunc() -- 你好,我是func!
5、闭包:
闭包是指一个函数能够访问其外部作用域中的变量,即使该外部函数已经执行完毕并返回
如下面的代码中,改变了外部函数传入参数 x 的生命周期:
function Func(x)
return function(y)
return x + y
end
end
myfunc = Func(10) -- 10这个变量的生命周期被改变
print(myfunc(5)) -- 15
6、几个注意点:
1、函数是一种类型,即 function,因此能赋值给变量
2、Lua 中,不支持函数重载,默认调用最后一次声明的函数
八、表
table 是 Lua 的一种数据结构,是 lua 中非常重要的一个知识点,table 用来帮助我们创建不同的数据类型,如:数组、字典、类等,学习 table 对我们理解 lua 有非常大的帮助。
Lua 中例如字典和类这些并非真正 Lua 的语法,只不过是用 table 的特性模拟的,所以我下面的数据结构都称为 “模拟 XX”。
(一)Lua 模拟数组
使用 {} 包裹起来的数据构成了数组,其中数组中的值可以是任意数据类型,但不要填 nil
使用 {} 包裹多个数组可以构成二维数组。
1、数组的遍历:
Lua 中的数组下标是从 1 开始的,而不是 0!使用#可以获取数组长度,结合这两点就可以做到数组的遍历:
(但实际遍历时不推荐使用#,后面会解释)
-- 数组定义
a = {1,"123",true,2}
print(a[0]) -- nil,数组索引从1开始
print(a[1]) -- 1
print(a[3]) -- true
-- 遍历数组
for i=1,#a do
print(a[i]) -- 1 123 true 2
end
-- 二维数组定义
a = {{1,2,3},{4,5,6}}
print(a[2][2]) -- 5
-- 遍历二维数组
for i=1,#a do
b = a[i]
for j=1,#b do
print(b[j]) -- 1 2 3 4 5 6
end
end
2、为什么不推荐使用 #:
先看 table 长度的定义:table 的长度被定义成一个整数下标 n ,它满足 t [n] 不是 nil 而 t [n+1] 为 nil。
当数组中间出现了 nil,即出现了 “空洞”,使用#获取数组的长度是未定义的,即无法知道结果到底是什么。# 获取的结果可能是指向任何一个是 nil 值的前一个位置的下标,获取的长度是不确定的。
详细解释可看下面这篇文章:
3、自定义索引
可以自己在数据前自定义一个索引,当然设置比较奇怪的索引可能会影响#获取数组长度(即出现上述所说的出现空洞的问题)。
a = {[0]=1,2,3,[-1]=4,5} -- 0号索引设置为1,-1号索引设置为4
print(a[-1]) -- 4
print(a[0]) -- 1
print(a[1]) -- 2
print(a[2]) -- 3
print(a[3]) -- 5
print(#a) -- 3,长度计算还是按正常的索引顺序算,即只计算了2、3、5这三个数
4、迭代器遍历
为了解决之前所说的用 #遍历数组带来的问题,接下来介绍两种迭代器遍历方式:
ipairs 和 pairs:
ipairs 的局限就是只能遍历索引从 1 开始的,而且中间断序了,即获取到 nil 时就结束了
pairs 可以找出表中所有的键,推荐使用这个
arr = {[-1]=1,[0]=2,3,4,5,[5]=6}
-- 1、ipairs遍历
for i,k in ipairs(arr) do
print("索引:"..i.." 值:"..k)
--[[
索引:1 值:3
索引:2 值:4
索引:3 值:5
]]
end
-- 2、pairs遍历
for i,k in pairs(arr) do
print("索引:"..i.." 值:"..k)
--[[
索引:1 值:3
索引:2 值:4
索引:3 值:5
索引:0 值:2
索引:-1 值:1
索引:5 值:6
]]
end
(二)Lua 模拟字典
1、基本操作:
-- 1、字典基本操作
-- 声明
a = {["name"]="Tom",["age"]=12}
-- 访问
print(a["age"]) -- 12
print(a.name) -- Tom,通过类似类变量的方式访问
-- 修改
a["age"] = 18
-- 添加
a["sex"] = false
-- 删除
a["sex"] = nil
-- 2、遍历字典
for k,v in pairs(a) do
print(k,v)
--[[
name Tom
age 18
]]
end
2、几个注意点:
① 字典的删除操作可以考虑将值置 nil
② 字典的值可以通过点出来使用,不过只对键是字符串时有效
(三)Lua 模拟类
1、类内部声明:
类的每个属性和方法本质上就是表中的一个键值对,因此根据表的特性(字典的添加和修改操作),类的属性和方法不仅能在表内声明,还能在表外部进行声明,下面先看类内部声明吧。
注意几点:
(1)由于这并非真正的类,因此想在方法内部调用自身的属性,并不能直接调用,Lua 也没有 this 关键字,因此要么通过类名调用(表名),要么通过传参数进来进行调用,详情看下面的 PrintAge 方法。
(2)目前这样声明的类并非完全具有面向对象的特性,更像是拥有静态属性和静态方法的类,而面向对象相关知识点会在后面章节讲述。
-- 类声明
Student = {
-- 年龄
age = 18,
-- 性别
sex = true,
-- 成长方法
Up = function()
print("Up")
end,
-- 打印年龄方法
PrintAge = function(s)
-- print(age) -- 这个age和表中的age没有任何关系,这代表一个全局变量
-- print(Student.age) -- 方法一:必须指定表名才可以调用正确的属性和方法
print(s.age) -- 方法二:外部传一个参数进来
end
}
2、成员和方法调用:
这一小节和第 3 小节都有一些新知识点,不过别急,在第 4 小节会对新知识点进行简单总结。
-- 调用类属性和方法
print(Student.age) -- 18
Student.Up() -- Up
Student.PrintAge(Student) -- 打印18,使用点调用方法必须传入参数
Student:PrintAge() -- 打印18,使用冒号调用方法,默认把调用者作为第一个参数传入方法中
3、类外部声明:
在类外部,可以声明成员变量和方法,而方法的声明有多种方式
-- 外部声明变量或方法
Student.name = "Tom" -- 外部声明成员变量
Student.Func = function() -- 外部声明函数方式1
print("Func")
end
function Student.Func2() -- 外部声明函数方式2
print("Func2")
end
function Student:Func3() -- 外部声明函数方式3,使用冒号
print(self.name .. " Func3") -- lua中,self表示默认传入的第一个参数,和this不是一个东西
end
-- 调用类外部声明的方法
Student.Func3(Student) -- Tom Func3,这里必须要传一个参数,因为函数中使用了self这个参数
Student:Func3() -- Tom Func3
4、新知识点总结:
在模拟类中,我们又新学到了几个知识点,都比较重要,下面进行简单的总结:
(1)函数中的冒号与点号
函数调用时,使用点号就是普通的函数调用,而使用冒号调用默认将调用者作为第一个参数传入函数。
为表声明函数时,使用点号代表正常的声明函数,而使用冒号则默认为函数添加一个名为 self 的隐式参数作为第一个参数。
综上,我简单总结一下冒号的特殊之处:冒号代表着是否默认带上一个参数
(2)self
Lua 中的 self 和其他语言的 this 不是同一种概念!self 代表默认传入的第一个参数,一般搭配冒号声明的函数使用。最好理解这一点,方便后续面向对象的学习
(四)表的操作

下面简单通过代码使用一下:
t1 = {{age=1,name="tom"},{age=2,name="tony"}}
t2 = {name="jany",sex=false}
-- 1、插入
table.insert(t1,t2) -- 默认t2插入到t1后面
print(t1[1].name) -- tom
print(t1[2].name) -- tony
print(t1[3].name) -- jany
-- 2、移除
table.remove(t1) -- 默认移除最后一个
print(t1[1].name) -- tom
print(t1[2].name) -- tony
print(t1[3]) -- nil
-- 3、排序
t3 = {1,5,8,3,6,2}
table.sort(t3)
for _,v in pairs(t3) do
print(v) -- 1 2 3 5 6 8
end
-- 自定义排序规则
table.sort(t3, function(a,b)
if a > b then
return true -- 希望a排b前面则返回true
end
end)
for _,v in pairs(t3) do
print(v) -- 8 6 5 3 2 1
end
-- 4、拼接
t4 = {"123","456","789"}
str = table.concat(t4, ";")
print(str) -- 123;456;789