基础语法
基本语法
注释
单行注释
两个减号是单行注释:
--多行注释
--[[
多行注释
多行注释
--]]标示符
Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
最好不要使用下划线加大写字母的标示符,因为 Lua 的保留字也是这样的。
Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。因此在 Lua 中 Test 与 test 是两个不同的标示符。以下列出了一些正确的标示符:
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal关键词
以下列出了 Lua 的保留关键词。保留关键字不能作为常量或变量或其他用户自定义标示符:
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
全局变量
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
如果你想删除一个全局变量,只需要将变量赋值为nil。
这样变量b就好像从没被使用过一样。换句话说, 当且仅当一个变量不等于nil时,这个变量即存在。
数据类型
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 函数测试给定变量或者值的类型:
nil(空)
nil 类型表示一种没有任何有效值,它只有一个值 -- nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:
对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:
nil 作比较时应该加上双引号 ":
type(X)==nil 结果为 false 的原因是 type(X) 实质是返回的 "nil" 字符串,是一个 string 类型:
boolean(布尔)
boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:
number(数字)
Lua 默认只有一种 number 类型 -- double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),以下几种写法都被看作是 number 类型:
string(字符串)
字符串由一对双引号或单引号来表示。
也可以用 2 个方括号 [[]] 来表示"一块"字符串。
在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:
以上代码中"error" + 1执行报错了,字符串连接使用的是 .. ,如:
使用 # 来计算字符串的长度,放在字符串前面,如下实例:
table(表)
在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是 {},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始。
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
function(函数)
在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里:
function 可以以匿名函数(anonymous function)的方式通过参数传递:
thread(线程)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata(自定义类型)
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
变量
变量在使用前,需要在代码中进行声明,即创建该变量。
编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
赋值语句
赋值是改变一个变量的值和改变表域的最基本的方法。
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
遇到赋值语句 Lua 会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
当变量个数和值的个数不一致时,Lua 会一直以变量个数为基础采取以下策略:
上面最后一个例子是一个常见的错误情况,注意:如果要对多个变量赋值必须依次对每个变量赋值。
多值赋值经常用来交换变量,或将函数调用返回给变量:
f() 返回两个值,第一个赋给 a,第二个赋给 b。
索引
对 table 的索引使用方括号 []。Lua 也提供了 . 操作。
流程控制
while
Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。
statements(循环体语句) 可以是一条或多条语句,condition(条件) 可以是任意表达式,在 condition(条件) 为 true 时执行循环体语句。
for
数值for循环
Lua 编程语言中 for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。
var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。
for 的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的 f(5) 只会在循环开始前执行一次,其结果用在后面的循环中。
泛型for循环
泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。
i 是数组索引值,v 是对应索引的数组元素值。ipairs 是 Lua 提供的一个迭代器函数,用来迭代数组。
repeat...unti
Lua 编程语言中 repeat...until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat...until 循环的条件语句在当前循环结束后判断。
循环嵌套
break 语句
Lua 编程语言 break 语句插入在循环体中,用于退出当前循环或语句,并开始脚本执行紧接着的语句。
如果你使用循环嵌套,break语句将停止最内层循环的执行,并开始执行的外层的循环语句。
goto 语句
Lua 语言中的 goto 语句允许将控制流程无条件地转到被标记的语句处。
if 语句
Lua if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
以下实例用于判断变量 a 的值是否小于 20:
if...else 语句
Lua if 语句可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
在布尔表达式为 true 时会 if 中的代码块会被执行,在布尔表达式为 false 时,else 的代码块会被执行。
Lua 认为 false 和 nil 为假,true 和非 nil 为真。要注意的是 Lua 中 0 为 true。
以下实例用于判断变量 a 的值
elseif
Lua if 语句可以与 elseif...else 语句搭配使用, 在 if 条件表达式为 false 时执行 elseif...else 语句代码块,用于检测多个条件语句。
以下实例对变量 a 的值进行判断
函数
在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。
Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如 print() 函数可以将传入的参数打印在控制台上。
Lua 函数主要有两种用途:
完成指定的任务,这种情况下函数作为调用语句使用;
计算并返回值,这种情况下函数作为赋值语句的表达式使用。
Lua 编程语言函数定义格式如下:
以下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:
Lua 中我们可以将函数作为参数传递给函数,如下实例:
多返回值
Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。
Lua函数中,在return后列出要返回的值的列表即可返回多值,如:
可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。
我们可以将可变参数赋值给一个变量。
例如,我们计算几个数的平均值:
也可以通过 select("#",...) 来获取可变参数的数量:
有时候我们可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …) 或者 select(n, …)
select('#', …) 返回可变参数的长度。
select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。
调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。
运算符
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua 提供了以下几种运算符类型:
算术运算符
关系运算符
逻辑运算符
其他运算符
..连接两个字符串#一元运算符,返回字符串或表的长度。
运算符优先级
文件 I/O
Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。
简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
完全模式(complete model) 使用外部的文件句柄来实现。它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法
简单模式
简单模式在做一些简单的文件操作时较为合适。但是在进行一些高级的文件操作的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操作,使用完全模式则较为合适。
以下为 file.lua 文件代码,操作的文件为test.lua(如果没有你需要创建该文件),代码如下:
执行以上代码,你会发现,输出了 test.lua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释
在以上实例中我们使用了 io."x" 方法,其中 io.read() 中我们没有带参数
"*n"读取一个数字并返回它。例:file.read("*n")"*a"从当前位置读取整个文件。例:file.read("*a")"*l"(默认) 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l")number 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)
其他的 io 方法有:
io.tmpfile():返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
io.type(file): 检测obj是否一个可用的文件句柄
io.flush(): 向文件写入缓冲中的所有数据
io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件
完全模式
通常我们需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件:
执行以上代码,你会发现,输出了 test.lua 文件的第一行信息,并在该文件最后一行添加了 lua 的注释。
以下实例使用了 seek 方法,定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,即从当期位置(倒数第 25 个位置)读取整个文件。
错误处理
程序运行中错误处理是必要的,在我们进行文件操作,数据转移及 web service 调用过程中都会出现不可预期的错误。如果不注重错误信息的处理,就会造成信息泄露,程序无法运行等情况。
语法错误
语法错误通常是由于对程序的组件(如运算符、表达式)使用不当引起的。一个简单的实例如下:
以上代码执行结果为:
正如你所看到的,以上出现了语法错误,一个 "=" 号跟两个 "=" 号是有区别的。一个 "=" 是赋值表达式两个 "=" 是比较运算。
另外一个实例:
执行以上程序会出现如下错误:
语法错误比程序运行错误更简单,运行错误无法定位具体错误,而语法错误我们可以很快的解决,如以上实例我们只要在 for 语句下添加 do 即可:
运行错误
运行错误是程序可以正常执行,但是会输出报错信息。如下实例由于参数输入错误,程序执行时报错:
当我们编译运行以下代码时,编译是可以成功的,但在运行的时候会产生如下错误:
lua 里调用函数时,即使实参列表和形参列表不一致也能成功调用,多余的参数会被舍弃,缺少的参数会被补为 nil。
以上报错信息是由于参数 b 被补为 nil 后,nil 参与了 + 运算。
假如 add 函数内不是 "return a+b" 而是 "print(a,b)" 的话,结果会变成 "10 nil" 不会报错。
错误处理
我们可以使用两个函数:assert 和 error 来处理错误。实例如下:
执行以上程序会出现如下错误:
实例中 assert 首先检查第一个参数,若没问题,assert 不做任何事情;否则,assert 以第二个参数作为错误信息抛出。
error函数
功能:终止正在执行的函数,并返回 message 的内容作为错误信息 (error 函数永远都不会返回)
通常情况下,error 会附加一些错误位置的信息到 message 头部。
Level 参数指示获得错误的位置:
Level=1[默认]:为调用 error 位置 (文件 + 行号)
Level=2:指出哪个调用 error 的函数的函数
Level=0: 不添加错误位置信息
pcall 和 xpcall、debug
Lua 中处理错误,可以使用函数 pcall(protected call)来包装需要执行的代码。
pcall 接收一个函数和要传递给后者的参数,并执行,执行结果:有错误、无错误;返回值 true 或者或 false, errorinfo。
语法格式如下
pcall 以一种 "保护模式" 来调用第一个参数,因此 pcall 可以捕获函数执行中的任何错误。
通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置。但 pcall 返回时,它已经销毁了调用桟的部分内容。
Lua 提供了 xpcall 函数,xpcall 接收第二个参数——一个错误处理函数,当错误发生时,Lua 会在调用桟展开(unwind)前调用错误处理函数,于是就可以在这个函数中使用 debug 库来获取关于错误的额外信息了。
debug 库提供了两个通用的错误处理函数:
debug.debug:提供一个 Lua 提示符,让用户来检查错误的原因
debug.traceback:根据调用桟来构建一个扩展的错误消息