01-Lua语言入门
Lua是一门简明、可扩展、可移植且高效的脚本语言,最初由巴西的里约热内卢天主教大学的一个团队开发。Lua语言的设计目标是为了嵌入到应用程序中,提供灵活的扩展能力。Lua具有简单的语法、动态类型、自动内存管理和高性能等特点,使其在游戏开发、嵌入式系统和脚本编程等领域得到广泛应用。
环境配置与Hello world
在配置好Lua环境后,便能编写经典的hello world了:
print("Hello World!") -- 输出 Hello World!
程序段
Lua语言执行的每一段代码(例如一个文件,或交互模式下的一行)称为一个 程序段(Chunk),即一组命令或表达式组成的序列。程序段既可以简单到只由一句表达式构成,也可以由多句表达式和函数定义组成。
可通过直接在交互模式下运行独立解释器,或将源码保存为.lua文件并运行dofile()来执行写好的程序段:
交互模式
要想使用交互模式,先在终端内输入lua以启用独立解释器,然后就能逐行执行你输入的代码了:
我这里为了区分版本,使用lua54。
在Lua5.3之前,需要在表达式前加上一个等号才能执行代码,如 = a + 2。
如果想退出交互模式,可以通过输入EOF控制字符或调用操作系统库的exit函数(即执行os.exit())退出。
如果想在执行完某文件的程序段后启动交互模式,只需在终端输入以下代码即可:
lua -i xxx.lua
dofile()
如果想在程序端中调用其他文件的代码,可以使用dofile()函数。例如,某库文件lib1.lua提供函数func1(),在交互模式中可以这样用:
dofile("lib1.lua")
func1() --> 调用lib1.lua中的func1()函数
词法规范
标识符
由任意字母(A-Z, a-z),数字和下划线组成的字符串(不能以数字开头)。其中,“下划线+大写字母”(如_VERSION)组成的标识符常被Lua用作特殊用途,应避免使用。
保留字
以下是Lua语言的保留字,它们不能被用作标识符:
and break do else elseif
end false goto for function
if in local nil not
or repeat return then true
until while
注释
print("123") -- 单行注释
--[[多行
长注释
]]
-- 多行注释的正确用法:
--[[ 弃用这段代码
print(10)
--]]
---[[ 启用这段代码
print(10)
--]]
变量
全局变量
在Lua中,全局变量无需声明即可使用,使用未经初始化的全局变量也不会导致错误,它的结果是nil:
b --> nil
b = 10
b --> 10
当把nil赋值给全局变量时,Lua会回收该变量占用的内存。
局部变量
Lua语言中的变量在默认情况下是全局变量 ,所有的局部变量在使用前必须用local声明。局部变量的生效范围仅限于声明它的代码块(block):一个代码块是一个控制结构的主体,或是一个函数的主体,也可以是一个代码段。
可以用do-end显式声明一个代码块:
local x1, x2
do
local a2 = 2 * a
local dt = (b^2 - 4 * a * c)^(1 / 2)
x1 = (-b + dt) / (a2)
x2 = (-b - dt) / (a2)
end -- a2和dt在此处已经失效
print(x1, x2) --> x1和x2仍然有效
尽可能地使用局部变量是一种良好的编程风格:
- 局部变量可以避免由于不必要的命名而造成全局变量的混乱;
- 局部变量还能避免同一程序中不同代码部分中的命名冲突;
- 访问局部变量比访问全局变量更快;
- 局部变量会随着其作用域的结束而消失,使得GC能够将其释放。
类型和值
Lua是一种动态类型语言,在这种语言中没有类型定义,每个值都带有其自身的类型信息。一共有八种基本类型:nil空类型,boolean布尔类型,number数值类型,string字符串类型,userdata用户数据类型,function函数类型,thread线程类型,table表类型。
可以使用type(x)获取变量x的类型:
type(nil) --> nil
type(true) --> boolean
type(10.4 * 3) --> number
type("Hello") --> string
type(io.stdin) --> userdata
type(print) --> function
type(type) --> thread
type({}) --> table
简单类型
先看看简单类型(nil和boolean),其他类型在后面会学到。
nil
只有nil值的类型,主要作用就是与其他所有值进行区分,Lua使用nil来表示无效值的情况。
boolean
只有两个值:true和false,它们代表了传统布尔值。但boolean值并非是用于条件测试的唯一方式,Lua将除了false和nil外的所有其它值视为真(这表示0和空字符串也是真)。
接下来看看Lua的逻辑运算符:
-
and:格式为
op1 and op2,如果op1为假,则返回op1;否则返回op2。4 and 5 --> 5nil and 13 --> nilfalse and 13 --> false -
or:格式为
op1 or op2,如果op1为真,则返回op1;否则返回op2。0 or 5 --> 0false or "hi" --> hinil or false --> false
and 和 or 都遵循短路求值原则,即只在必要时才对op2求值。
在Lua语言中,应用上述逻辑表达式的一些惯用写法如下:
x = x or v:等同于if not x then x = v end,即当x未被初始化时,将其默认值设为v。a and b or c:当b不为假时,就是C语言的a ? b : c,例如可以用(x > y) and x or y选出x和y中数值较大的一个。
-
not:格式为
not op,它永远返回Boolean类型的值:not nil --> truenot false --> truenot not nil --> false
控制结构
Lua提供了一组精简常用的控制结构:
- 用于条件执行的
if; - 用于循环的
while,repeat,for;
并且所有的控制结构语法上都有一个显式的终结符:
end用于终结if,for和while结构;until用于终结repeat结构。
if then else(if)
if语句会先测试其条件,根据条件是否满足执行相应的then部分或else(if)部分,其中else(if)部分是可选的。
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
end
Lua语言不支持switch语句,因此上述一连串的elseif语句是实现类似功能的唯一方式。
while
先测试条件是否为真,是的话就循环,否则循环结束。
local i = 1
while a[i] do
print(a[i])
i = i + 1
end
repeat
重复执行循环体直到条件为真时结束,由于条件测试在循环体之后执行,所以循环体至少会执行一次。
-- 牛顿迭代法求平方根
local sqr = x / 2
repeat
sqr = (sqr + x / sqr) / 2
local error = math.abs(sqr^2 - x)
until error < x / 10000
可以发现,循环体内声明的局部变量作用域包括测试条件。
for
for循环有两种形式:数值型和泛型。
数值型
数值型for的语法格式如下:
for var = st, ed [, step] do
sth
end
在循环中,var的值从st以步长step(可选,默认为1)增长至ed,每步执行一次sth。如果不想设置上限,可以让ed = math.huge。
要注意的是,变量var是被for语句自动声明的局部变量,它的作用域 仅限于循环体内 。此外,还可以使用break语句来提前结束循环。
泛型
泛型for遍历迭代器函数返回的所有值,它的功能非常强大,使用恰当的迭代器可以在保证代码可读性的情况下遍历几乎所有的数据结构。它的语法格式如下:
for var in 迭代器函数 do
sth
end
有关泛型for的内容详见后续章节。
break, return和goto
break语句可以结束循环;return语句用于返回函数的执行结果或简单地结束函数运行;goto语句则允许跳转到函数中的几乎任何地方。
这里看一下goto,它用于将当前程序跳转到相应的标签处继续执行。在使用goto跳转时,Lua语言设置了一些限制条件:
- 标签遵循常见的可见性规则,因此不能直接跳转到一个代码块中的标签(该标签对外不可见)
goto不能跳转到函数外goto不能跳转到局部变量的作用域
goto语句在编写状态机时也很有用,如下代码用于检验输入是否包含偶数个0:
-- 标签语法: ::标签名::
::s1:: do
local c = io.read(1)
if c == '0' then goto s2
elseif c == nil then print 'ok'; return
else goto s1
end
end
::s2:: do
local c = io.read(1)
if c == '0' then goto s1
else if c == nil then print 'not ok'; return
else goto s2
end
end
goto s1
参考资料
- 《Lua程序设计 (第4版)》
- VSCode 搭建 Lua 开发环境 - 千叶Christa
- 【唐老狮】Unity热更新之Lua语法
