Lua是一种灵活小巧的脚本语言,可以被嵌入应用程序中,为应用程序提供灵活的扩展和定制功能。
注释
单行注释
1 | -- 单行注释 |
多行注释
1 | --[[ |
变量
变量标识符
Lua的变量标识符用大小写字母、下划线、数字等组成,必须用字母或下划线作为开头。最好不要使用下划线加大写字母的标示符,以免与Lua的保留字冲突。在Lua的变量标识符中,字母需要区分大小写。一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION
等)被保留用于 Lua 内部全局变量,_G
表示全局作用域,后边还会提到。
变量作用域
Lua中作用域分为三种:全局作用域、局部作用域、表内作用域。声明变量时会默认使用全局作用域;声明局部变量需要显式的在变量名之前加上Local
关键字。表(table)中存储的是键值对,可以根据键名来获取对应的值。
1 | var1 = 1 -- 全局 |
全局变量可以看做在名为_G
的表中的变量。
基本数据类型
在Lua中使用变量不需要提前声明,变量的类型决定于用户赋值的类型。可以使用type()
函数判断变量的类型。
Lua中有8种基本的数据类型:
- nil
- boolean
- string
- number
- table
- function
- thread
- userdata
nil
nil
类型表示没有有效值,当访问一个没有初始化的全局变量时并不会出错,而会得到nil
。
通过将全局变量和表内的变量赋值为nil
,可以达到释放该变量的目的。
1 | -- 引用未定义变量将返回 nil ,并不会出错 |
boolean
boolean 类型只有两个可选值:true
和false
,在Lua中false
和nil
被看作是“假”,其他的都为“真”。
string
与python类似,字符串可以使用两个单引号'
或两个双引号"
包裹起来表示,也可以用多行注释来表示跨行的字符串。
1 | str = 'aspythonstring' -- 像 Python 一样不可变 |
对于[[ ... ]]
形式的字符串,可以两个方括号之间加相等数量的=
,如:
1 | str = [=[ |
使用 ..
来连接两个字符串:
1 | print("a" .. 'b') -- ab |
使用#
来获取字符串长度
1 | str = "hello" |
字符串的一些操作其他操作
1 | string.upper(argument) -- 将所有的小写字母转为大写 |
number
Lua中的number都是双精度的实数值。整数同样也用number表示。
table
Lua中的table是一种关联数组(associative array),其中存储的是键值对。也可以仅指定值而使用默认的数字索引作为键。Lua的表中默认初始索引从 1 开始。table是一种非常重要的数据类型,后边专用一节细讲。
function
表示由C或Lua编写的函数。Lua中的function是“第一类值”,可以存储在变量中,可以作为参数或返回值,后边详细介绍。
thread
表示执行的独立线路,用于执行协同程序(coroutine)。
userdata
表示任意存储在变量中的C数据结构。
控制流
Lua中的代码块以总是以end
结尾(函数的定义也用end
表示结束)。在Lua中有以下一些流程控制语句。
分支语句
使用if ... then ... else
来表示分支结构。除了nil
和false
之外,其它的值(包括数字0
和空字符串''
等)均为“真”。
逻辑的与、或、非用and
、or
、not
表示,判断相等、不等使用==
、~=
。
if
判断可以多分支和嵌套,相关语句使用方式参见下例:
1 | if a <= 0 |
某些情况下可以使用以下的方式来达到C语言中问号表达式的效果:
1 | ans = aBoolValue and 'yes' or 'no' |
循环语句
Lua中的循环有以下四种方式:
while…do循环
与C语言中相似,详见下边代码,注意在Lua中没有自增和自减运算符:
1 | while num < 50 do |
for循环
for
循环指定起止的数值及步长,循环变量可以取到起止的值,当不指定迭代步长时步长使用默认值1。
1 | sum = 0 |
泛型for循环
遍历数组,对其中的每一个元素进行循环内的操作。Lua中使用迭代器函数ipairs
来遍历访问数组,详见示例代码:
1 | days = {"Suanday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"} |
其中i
是数组索引值,v
是对应索引的数组元素值。
对于非数组形式的table,使用迭代器函数pairs
来遍历访问,详见示例代码:
1 | colors = {red = "FF0000", green = "00FF00", blue = "0000FF"} |
其中其中k
是键,v
是对应的值。
repeat…until 循环
repeat
和until
之间的语句至少会执行一次。
1 | rest = 10 |
函数
函数是对语句和表达式进行抽象的主要方法,既可以用来处理一些特殊的工作,也可以用来计算一些值。
函数定义
Lua中的函数定义方式如下:
1 | [作用域] function 函数名( 参数1, 参数2, 参数3..., 参数n) |
函数的作用域有局部与全局之分。定义局部函数使用local
关键字,定义表内的函数直接将函数指定给表内对应的键。
以下的示例代码定义了一个求两个数中最大值的函数:
1 | function max(num1, num2) |
以下的示例代码使用函数递归调用来实现斐波那契数列求和:
1 | function fib(n) |
Lua中的函数是“第一类值”,函数可以保存为变量。
1 | function f(x) return x * x end |
高阶函数
1 | myprint = function(param) |
输出为
1 | 这是打印函数 - ## 10 ## |
可变参数
函数可以接受可变数量的参数,在函数参数列表中使用三点(…
),表示函数有可变的参数。Lua将函数的参数放在一个叫arg
的表中,#arg
可以计算传入参数的个数。
以下示例代码用来计算几个数的平均值:
1 | function average(...) |
多返回值
Lua中的函数可以有多个返回值,返回值之间用逗号隔开,如下示例:
1 | function func(a, b, c) |
表
表是Lua中唯一的复合类型,本质是关联数组,可用来构建不同的数据类型,如数组、字典等。table中的键(数组索引)可以使用除nil
外任意类型的值。table的大小是不固定的,你可以根据自己需要进行扩容。
构造
表的构造有以下三种形式:
1 | tab1 = {} -- 构造空表 |
对于上边的第三种方式,当键为字符串时,可以省略方括号和引号,所以下边的代码与上边的第三行等价:
1 | tab3 = { key1 = "val1", key2 = "val2", [3] = "val3"} -- 指定键和值 |
Lua中的全局变量其实是存储在一个名为_G
的table中,所以,以下的代码会输出true
。
1 | print(_G['_G'] == _G) -- 输出true |
Lua中table有以下的方法:
1 | table.concat (table [, sep [, start [, end]]]) |
数组
table中省略键时,变成使用默认数字索引的数组,索引值是从1开始的。
1 | v = {'value1', 'value2', 1.21, 'gigawatts'} |
metatable
元表(metatable)可以看做表的表,类似 JavaScript中原型(prototype)的概念。使用元表(metatable)可以改变table的行为,table的每个行为都关联了对应的元方法(metamethod)。主要涉及两个方法:
setmetatable(table,metatable)
:对指定table设置元表,如果table的元表中已存在__metatable
键,setmetatable
则会失败 。为一个表的元表增加__metatable
键可以保护和锁定该表中的元方法;getmetatable(table)
:返回table的元表。
元表能够被用于定义算术运算符和关系运算符的行为。以下是一段示例代码,为表定义了加法运算。
1 | f1 = {a = 1, b = 2} |
Lua中的值都具有元方法,表中的元方法可以被重载,以下是表中可以被重载的元方法:
元表的键 | 重载的方法 |
---|---|
__add(a, b) | a + b |
__sub(a, b) | a - b |
__mul(a, b) | a * b |
__div(a, b) | a / b |
__mod(a, b) | a % b |
__pow(a, b) | a ^ b |
__unm(a) | -a |
__concat(a, b) | a .. b |
__len(a) | #a |
__eq(a, b) | a == b |
__lt(a, b) | a < b |
__le(a, b) | a <= b |
__index(a, b) (可以是函数或表) | a.b |
__newindex(a, b, c) | a.b = c |
__call(a, …) | a(…) |
例如重载表中根据键取值的符号"."
。元表中的__index
可以是一个表或一个函数,当通过键来访问表的时候:
- 如果这个键有值,则会返回对应的值;
- 如果这个键没有值,且该表没有元表,或该表有元表但元表中没有
__index
键,则返回nil
; - 如果这个键没有值,但该表有元表且元表中
__index
键对应了一个表,则会在对应的表中查找对应该键的值; - 如果这个键没有值,但该表有元表且元表中
__index
键对应了一个函数,则会把表和键一起传入函数,返回函数的结果
1 | defaultFavs = {animal = 'gru', food = 'donuts'} |
table实现对象与继承
Lua中的表可以作为对象,为了使由表实现的对象具有独立的生命周期,同样的方法(函数)可以对不同的对象单独作用,Lua中使用self
关键字,用于区分函数作用的对象。self
表示函数调用者,使用冒号":"
可以在生命或调用函数时隐藏self
参数,但是在函数内部仍然可以使用self
。以下的两行代码等价:
1 | function tab1.fun1(tab1,arg1) -- ...略去剩余部分 |
在调用时也是同样规律,以下两行代码等价:
1 | r = tab1.fun1(tab1, 100) |
以下是定义类与创建对象的一段代码:
1 | Shape = {area = 0} |
以下是类继承的一个示例:
1 | -- 父类 |
其它
多个变量同时赋值
赋值语句可以同时对逗号分隔的多个值赋值。当变量数不足时,多余的值被舍弃;当值不足时,变量被赋值为nil
。
1 | a, b = 10, 20 -- 相当于 a=10; b=20 |
闭包
匿名函数与闭包:
1 | function adder(x) |
在泛型for循环中,使用自定义的闭包实现带状态的迭代器:
1 | array = {"Lua", "Tutorial"} |
协程
协程(coroutine)与线程比较类似:协程拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。与线程不同,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
以下是协程相关的一些方法:
1 | coroutine.create() |
参看以下的一段示例代码:
1 | function foo (a) |
首次调用resume
时,resume
中除了第一个参数之外,后边的参数会传给创建协程的函数。当协程被挂起回到主线程,再次调用resume
时,resume中除了第一个参数之外后边的参数会通过yield
传回到协程中。上边的代码输出结果如下:
1 | co-body 1 10 |
模块
Lua 的模块是由变量、函数等已知元素组成的table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个table就行。
require
以下是文件mod.lua:
1 | -- 文件名 module.lua |
在主文件中使用前边定义的模块module.lua:
1 | require("module") -- 使用require引用 |
引用模块也可以写成以下的形式:
1 | require "<module>" |
require
的特点是仅加载一次,并且对于模块会按照特定的搜索规则去查找文件并加载。如果被包含的模块中有可执行的函数,多次require
,被调用文件只运行一次。如以下文件mod1.lua:
1 | -- 文件名 mod1.lua |
主文件中两次包含:
1 | local a = require("mod1") -- 会输出 'Hi!' |
dofile
使用dofile
不会缓存被包含的文件,所以对于之前的mod1.lua:
1 | dofile("mod1") -- 会输出 'Hi!' |
loadfile
loadfile
会编译代码,将整个模块文件当成一个函数返回,但是不会执行代码。
1 | f = loadfile('mod1.lua') |
loadstring
loadstring
与loadfile
相似,是以字符串的形式读取一段代码。
1 | f = loadstring("print('Hi!')") |
loadlib
loadlib
用于加载C的库,一般是*.so或*.dll库。需要传入两个参数,一个是库的路径,一个是初始化函数,
1 | local path = "/usr/local/lua/lib/libluasocket.so" |
loadlib
函数加载指定的库并且连接到Lua,但并不打开库(也就是说没有执行初始化函数),反之它返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用。
REFERENCE
http://www.runoob.com/lua/lua-tutorial.html
http://tylerneylon.com/a/learn-lua/
Lua程序设计(第二版)