Lua 初级教程
- Lua
- [Lua 数据类型](#Lua 数据类型)
- 变量
- [Lua 循环](#Lua 循环)
-
- [while 循环](#while 循环)
- [for 循环](#for 循环)
- [repeat...until 循环](#repeat...until 循环)
- 循环嵌套
- [break 语句](#break 语句)
- [if...else 语句](#if...else 语句)
- 函数
- [Lua 数组](#Lua 数组)
- [Lua 模块与包](#Lua 模块与包)
Lua
lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false) |
boolean | 包含两个值:false和true |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表 |
可以使用 type 函数测试给定变量或者值的类型:
lua
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(nil))) --> string
print(type(type(X))) --> string
nil(空)
nil 类型表示一种没有任何有效值,它只有一个值 -- nil,例如打印一个没有赋值的变量,便会输出一个 nil 值。对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉。
boolean(布尔)
boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true。
number(数字)
对于数字,Lua 默认只有一种 number 类型。例如 2,2.2,0.2,2.35e+8,0.2e-1,7.8263692594256e-06 这些都是number类型。
string(字符串)
字符串由一对双引号或单引号来表示。也可以用 2 个方括号 "[[]]" 来表示"一块"字符串。
lua
str = [[
this is a string
this is a string
]]
print(str)
>>>输出结果
this is a string
this is a string
在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字。字符串连接使用的是两个小数点。
lua
print("2" + 6) --> 8
print("2" + "6") --> 8
print("2 + 6") --> 2 + 6
print("-2e2" * "6") --> -1200
print("error" + 1) --> 报错
print("error" .. 1) --> error1
print("error" .. "1") --> error1
print(1 .. 1) --> 11
print("1" .. "1") --> 11
使用 # 来计算字符串的长度,放在字符串前面。(补充一下,注释代码用两个减号,不能用井号和双斜杠)
lua
a = "abcdefg"
print(#a) --> 7
print(#"xyz") --> 3
table(表)
在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表。
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
lua
-- 创建一个空的 table
local tbl1 = {}
-- 直接初始表
local tbl = {"apple", "orange", "grape"}
print(tbl1)
print(tbl)
tbl[-1] = "National"
tbl[1] = nil
tbl[2] = "Tomato"
tbl["key1"] = "value1"
print(#tbl)
print("============")
for k, v in pairs(tbl1) do
print(k .. " : " .. v)
end
print("============")
for k, v in pairs(tbl) do
print(k .. " : " .. v)
end
>>>输出结果
table: 00C99200
table: 00C99700
0
============
============
3 : grape
2 : Tomato
-1 : National
key1 : value1
function(函数)
在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里。
lua
-- function_test.lua 脚本文件
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))
>>>输出结果
120
120
function 可以以匿名函数(anonymous function)的方式通过参数传递。
lua
-- function_test2.lua 脚本文件
function testFun(tab,fun)
for k ,v in pairs(tab) do
print(fun(k,v))
end
end
tab={key1="val1",key2="val2"};
testFun(tab,
function(key,val)--匿名函数
return key.."="..val;
end
)
>>>输出结果
key1=val1
key2=val2
thread(线程)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata(自定义类型)
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
变量
应该尽可能的使用局部变量,有两个好处:
- 避免命名冲突。
- 访问局部变量的速度比全局变量更快。
lua
-- test.lua 文件脚本
a = 5 -- 全局变量
local b = 5 -- 局部变量
function joke()
c = 5 -- 全局变量
local d = 6 -- 局部变量
print(a, b, c, d) --> 5 5 5 6
end
joke()
print(c,d) --> 5 nil
print(a, b, c, d) --> 5 5 5 nil
do
local a = 6 -- 局部变量
b = 6 -- 对局部变量重新赋值
print(a,b); --> 6 6
end
print(a,b) --> 5 6
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以不引入第三变量就能对变量的值进行交换。
lua
a, b = 10, 20 --> 等价于 a=10; b=20
x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[j]'
赋值的时候,等号两边的元素数量不匹配
lua
a, b, c = 0, 1
print(a,b,c) --> 0 1 nil
a, b = 3, 4, 5
print(a,b) --> 3 4
a, b, c = 0
print(a,b,c) --> 0 nil nil
a, b, c = 0, 0, 0
print(a,b,c) --> 0 0 0
Lua 循环
while 循环
lua
-- 输出10到19
a=10
while( a < 20 )
do
print(a)
a = a+1
end
for 循环
lua
-- 输出-10到10(一共21个数)
for i=-10,10 do
print(i)
end
-- 输出 -10到10之间的偶数(一共11个数)
for i=-10,10,2 do
print(i)
end
-- 输出10到1
for i=10,1,-1 do
print(i)
end
-- 不进入循环体,所以不打印数字111
for i=1,10,-1 do
print(111)
end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组
lua
a = {key1 = "value1", key2 = "value2", key3 = "value3"}
-- 打印表a的键和值
for k, v in pairs(a) do
print(k, v)
end
b = {"apple", "banana", "orange"}
-- 打印数组b的索引和值
for i, v in pairs(b) do
print(i, v)
end
-- 下面的写法会报错
for i in a do
print(i)
end
repeat...until 循环
repeat...until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat...until 循环的条件语句在当前循环结束后判断。
lua
repeat
statements
until( condition )
循环条件判断语句(condition)在循环体末尾部分,所以在条件进行判断前循环体都会执行一次。
如果条件判断语句(condition)为 false,循环会重新开始执行,直到条件判断语句(condition)为 true 才会停止执行。
lua
-- 输出10到15(如果交换循环体里的两行代码,打印结果为11到16)
a = 10
repeat
print(a)
a = a + 1
until( a > 15 )
结构与do while 循环类似。不管条件如何都会进入循环体一次。不同的是,当until 的条件判断为 true 时会停止执行。
循环嵌套
lua
-- 打印九九乘法表
for i = 1,9
do
local line = ""
for j = 1,i
do
line = line .. " ".. (j.."x"..i.."="..i*j)
end
print(line)
end
break 语句
break 语句插入在循环体中,用于退出当前循环或语句。
如果使用循环嵌套,break语句将停止最内层循环的执行,并开始执行外层的循环语句。
lua
-- 输出1到5
for i = 1,10 do
print(i)
if (i == 5) then
break
end
end
if...else 语句
lua
a = 50
if (a == 10) then
print("a 的值为 10")
elseif (a == 20) then
print("a 的值为 20")
elseif (a == 30) then
print("a 的值为 30")
else
print("没有匹配 a 的值")
end
print("a 的真实值为: ", a)
>>>输出结果
没有匹配 a 的值
a 的真实值为: 50
lua中不支持switch-case语句,我们可以使用if elseif来代替
lua
a = "Sunday"
if (a == "Monday") then
print("星期一")
elseif (a == "Tuesday") then
print("星期二")
elseif (a == "Wednesday") then
print("星期三")
elseif (a == "Thursday") then
print("星期四")
else
print("休息日")
end
函数
多个入参
lua
-- 函数返回两个值的最大值
function max(num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
-- 对所有参数求和
function add(...)
local sum = 0;
local args = {...} -- 将所有参数收集到一个表里
for i, v in ipairs(args) do
sum = sum + v
end
return sum
end
多参数的循环也可以这么写 for i, v in ipairs{...}
多返回值
lua
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
index, num = maximum({8,10,23,12,5});
通常在遍历变长参数的时候只需要使用 {...},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', ...) 或者 select(n, ...)
select('#', ...) 返回可变参数的长度。
select(n, ...) 用于返回从起点 n 开始到结束位置的所有参数列表。
调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 select为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。
lua
function myFun (...)
a = {...}
print(#a) --> 6
b = select(2,...) --函数返回一个列表。因为左侧变量只有一个,所以会将列表第一个值赋值给变量
print (b) --> 10
print(select("#", ...)) --> 6
print(select("3", ...)) --> nil 7 3 5
print(select(3, ...)) --> nil 7 3 5
end
myFun(8,10,nil,7,3,5);
Lua 数组
lua 索引默认从 1 开始。使用整数索引来访问数组元素,如果指定的索引没有值则返回 nil。除此外还可以以负数为数组索引值
lua
-- 创建一个数组
local myArray = {10, 20, 30, 40, 50}
-- 循环遍历数组
for i = -1, #myArray do
print(myArray[i])
end
>>>输出结果
nil
nil
10
20
30
40
50
lua
-- 将 -2 到 2 之间的五个整数,添加到数组中
array = {}
for i= -2, 2 do
array[i] = i
end
数组的增删改
lua
-- 创建一个数组
local myArray = {10, 20, 30, 40, 50}
-- 添加新元素到数组末尾
myArray[#myArray + 1] = 60
-- 修改数组元素(将20修改为25)
myArray[2] = 25
-- 删除第三个元素(将30删除)
table.remove(myArray, 3)
二维数组
lua
-- 依次将1到9写入数组中
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = 3*(i-1)+j
end
end
Table 函数
concat 拼接
lua
local myArray = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri"}
print(table.concat(myArray)) --> SunMonTueWedThuFri
print(table.concat(myArray, "-")) --> Sun-Mon-Tue-Wed-Thu-Fri
print(table.concat(myArray, "-", 3)) --> Tue-Wed-Thu-Fri
print(table.concat(myArray,"-",1,5)) --> Sun-Mon-Tue-Wed-Thu
insert 插入
lua
local myArray = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri"}
table.insert(myArray, "DayEnd") --> 数组末尾添加元素 DayEnd
table.insert(myArray, 2, "Day2") --> 数组第二个位置插入元素 Day2
remove 删除
lua
local myArray = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri"}
print(table.remove(myArray)) --> 删除数组最后一个元素,并返回 Fri
print(table.remove(myArray, 3)) --> 删除数组第三个元素,并返回 Tue
Lua 模块与包
lua
require("./utils/TimeUtil")
print(dateTime.year)
print(dateTime.name)
-- 函数有入参必须传,不写会报错。但如果函数没入参,参数传不传都可以
dateTime.fun1(2)
--dateTime.fun2() fun2是私有函数,无法调用
dateTime.fun3()
>>>输出结果
2025
dateTime
函数1打印参数a:2
函数2打印
TimeUtil.lua 文件内容
lua
-- 定义一个名为 dateTime 的模块
dateTime = {}
-- 定义两个常量
dateTime.year = 2025
dateTime.name = "dateTime"
-- 定义公有一个函数
function dateTime.fun1(a)
print("函数1打印参数a:" .. a)
end
-- 定义私有一个函数
local function fun2()
print("函数2打印")
end
-- 通过公有函数调用私有函数
function dateTime.fun3()
fun2()
end
return dateTime
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用。