前言
开发环境: Lua 5.4.2
IDE 选择:Sublime Text 3
为了在游戏开发的路上走得更远,开始学习Lua,据我了解Lua主要便于游戏热更,并且非常简单轻便。学习过程基于《Lua程序设计》第4版由梅隆魁译,出版于电子工业出版社。旨在记录一些和我所熟悉的内容的不同之处,可能并没有特别详细。在上述环境中完成书上的小练习。
1.1 程序段
Lua语言执行的每一段代码(一个文件或交互模式下的一行)称为一个程序段(Chunk),即一组命令或表达式组成的序列。
程序段既可以只有一句表达式构成,也可以由多句表达式和函数定义(实际是赋值表达式)组成。
当不带参数地调用lua时,输入的每一条指令都会在按下回车键后立即执行,在Windows下可以用ctrl-C或调用exit函数退出交互模式。
从Lua5.3版本开始,可以直接在交互模式下输入表达式,Lua语言会输出表达式的值,在5.3之前的老版本中,实现相同的效果需要在表达式之前加入一个等号。如果不想输出结果,可以在行末加入一个分号;。为了向下兼容,5.3也支持这种语法。要以代码段的方式运行代码(不在交互模式下),必须把表达式包含在函数print的调用中。
在交互模式下,Lua语言解释器会把我们输入的每一行当作完整的程序块或表达式来解释执行。但是,如果遇到不完整的一行,会等待直到输入完整后再解释执行。
我们可以使用-i参数让Lua语言解释器在执行完指定的程序段后进入交互模式:
% lua -i prog
其中prog为文件名,这对于调试和手工测试很有用。
另一种运行程序段的方式是调用函数dofile,该函数会立即执行一个文件。
% dofile("test.lua") --加载文件
1.2 一些词法规范
Lua语言中的标识符(名称)是由任意字母(A-Z和a-z)、数字和下划线组成的字符串,且不能以数字开头。
“下划线 + 大写字母”(例如_VERSION)组成的标识符通常被Lua语言用作特殊用途,应避免将其用作其他用途。通常会将“下划线 + 小写字母”用作哑变量(Dummy variable)。
以下是Lua语言的保留字(reserve word),不能被用作标识符。
and break do else elseif end false
goto for function if in local nil not
or repeat return then true until while
Lua语言是对大小写敏感的,因此虽然and是保留字,但是AND和And就是两个不同的标识符。
Lua语言中使用两个连续的连字符(–)表示单行注释的开始(从–之后直到此行结束都是注释),使用两个连续的连字符加两对连续的左方括号表示长注释或多行注释的开始(直到两个连续的右括号为止,中间都是注释。
--[[多行
长注释
]]
在注视一段代码的时候,一个常见的技巧是将这些代码放在–[[和–]]之间,当我们需要重新启用这段代码的时候,只需要在第一行行首添加一个连字符即可:
---[[
print(10) -->10
--]]
因此最后一行实际上也是一条独立的单行注释。
在Lua语言中,连续语句之间的分隔符不是必需的,有需要也可以使用分号来进行分隔。
1.3 全局变量
在Lua语言中,全局变量(Global Variable)无须声明即可使用,使用未经初始化的全局变量也不会导致错误。当使用未经初始化的全局变量时,得到的结果是nil,当把nil赋给全局变量时,Lua会回收该全局变量(就像从未出现过一样)。
Lua语言不区分未初始化变量和被赋值为nil的变量。在执行b = nil后,Lua语言会最终回收该变量占用的内存。
1.4 类型和值
Lua语言是一种动态类型语言(Dynamically-typed language),在这种语言中没有类型定义(type definition),每个值都带有其自身的类型信息。
Lua语言中有8种基本类型:nil(空)、boolean(布尔)、number(数值)、string(字符串)、userdata(用户数据)、function(函数)、thread(线程)和table(表)。使用函数type可获取一个值对应的类型名称,其中type(type(X))是字符串。
userdata类型允许把任意的C语言数据保存在Lua语言变量中。在Lua语言中,用户数据类型除了赋值和相等性测试外,没有其他预定义的操作。用户数据被用来表示由应用或C语言编写的库所创建的新类型。例如,标准I/O库用userdata来表示打开的文件。在后面涉及C API的时候再讨论更多的相关内容。
一般情况下,将一个变量用作不同类型时会导致代码的可读性不佳;但是,在某些情况下谨慎地使用这个特性可以带来一定程度的便利。例如,当代码发生异常时可以返回一个nil值以区别于其他正常情况下的返回值。
1.4.1 nil
nil是一种只有一个nil值的类型,他的主要作用就是与其他所有值进行区分。Lua语言用nil来表示无效值的情况。
1.4.2 Boolean
Boolean值只有两个值,true和false。不过,在Lua语言中,任何值都可以表示条件。其中,将除了Boolean值的false和nil外的其他所有值视为真。特别的是,在条件检测中Lua语言把零和空字符串也都视为真。
Lua支持常见的逻辑运算符:and、or、not。其中and和or具有短路逻辑。即当and第一个操作数为假时,返回第一个操作数,当or的第一个操作数不为假时,返回第一个操作数,否则返回第二个操作数。
在Lua语言中,形如x=x or v的惯用写法非常有用,它等价于:
if not x then x = v end
即,当x未被初始化时,将其默认值设为v(x不是boolean类型的false)。
另一种有用的表达式形如((a and b) or c)或(a and b or c)(由于and运算符优先级高于or,所以这两个表达式等价),当b不为假时,它们还等价于C语言的三目运算符a?b:c。例如,我们可以使用表达式(x>y) and x or y选出x和y中较大的一个。
not运算符永远返回Boolean类型的值。
1.5 独立解释器
独立解释器(Stand-alone interpreter,由于源文件名为lua.c,所以也被称为lua.c,又由于可执行文件为lua,也被称为lua)是一个可以直接使用Lua语言的小程序。以下介绍几个参数。
如果源代码文件第一行以#开头,那么解释器在加载该文件时会忽略这一行。这个特征主要是为了方便在POSIX系统中将Lua作为一种脚本解释器来使用。
当Lua的独立解释器位于/usr/local/bin下,使用下列脚本:
#!/usr/local/bin/lua
或
#!/usr/bin/env lua
时不需要显式地调用Lua语言解释器也可以直接运行。
具体来说,在Unix-like系统(如Linux、macOS等)中,#!(称为shebang)是一个特殊的标记,位于脚本文件的第一行。它告诉操作系统这个脚本应该用哪种解释器来执行。通常情况下,如果没有shebang,当你想要运行一个Lua脚本时,你需要显式地调用Lua解释器,就像lua test.lua。但是,满足上述条件时,你可以通过给脚本文件添加可执行权限,然后直接运行脚本。例如,在终端中输入./test.lua(假设脚本在当前目录下)。
lua命令的完整参数形如:
lua [options] [script[args]]
其中,所有的参数都是可选的,当不使用任何参数的时候,就会直接进入交互模式。
-e参数允许我们直接在命令行中输入代码,例如:
% lua -e "print(math.sin(12))" -->-0.53657291800043
注意在POSIX系统下需要使用双引号,以防止Shell错误地解析括号。
-l参数用于加载库。-i参数用于在运行完其他命令行参数后进入交互模式。因此,下面的命令会首先加载lib库,然后执行x=10的赋值语句,并最终进入交互模式。
% lua -i -llib -e "x=10"
我们可以通过预先定义的全局变量arg来获取解释器传入的参数。
% lua script a b c
编译器在运行代码前会创建一个名为arg的表,其中存储了所有的命令行参数。索引0保存的内容为脚本名,索引1存的内容为第一个参数(本例中的’a’),以此类推;而在脚本之前的所有选项则位于附属索引上,例如:
% lua -e "sin=math.sin" script a b
解释器按照如下的方式获取参数:
arg[-3] = “lua”
arg[-2] = “-e”
arg[-1] = “sin=math.sin”
arg[0] = “script”
arg[1] = “a”
arg[2] = “b”
一般情况下,脚本只会用到索引为正数的参数。
Lua语言也支持可变长参数,可以通过可变长参数表达式来获取。在脚本文件中,表达式…(3个点)表示传递给脚本的所有参数。