Lua
基础语法
注释
Lua 的行注释为两个连续的减号,段注释以--[[开头,以--]]结尾。
运行
先Ctrl+S将代码保存为lua结尾的文件,F5运行lua文件。
数据类型
| 数据类型 | 描述 |
|---|---|
| nil | 只有值 nil 属于该类,表示一个无效值,与 Java 中的 null 类似。但在条件表达式中相当于 false。 |
| boolean | 包含两个值:false 和 true。 |
| number | 表示双精度类型的实浮点数。 |
| string | 字符串,由一对双引号或单引号括起来。当一个字符串包含多行时,可以在第一行中以[[开头,在最后一行中以]]结尾,那么在[[与]]括起来的这多行内容就是一个字符串。换行符为字符串”\n”。 |
| table | 类似于 Java 中的数组,但比数组的功能更强大,更灵活。 |
| function | 由 C 或 Lua 编写的函数。 |
| thread | 协同线程,是协同函数的执行体,即正在执行的协同函数。 |
| userdata | 一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据存储到 Lua 变量中调用。 |
保留字
- Lua 是动态类型语言,变量的类型可以随时改变,无需声明。
- Lua 是弱类型语言,变量无需类型声明即可直接使用。变量分为全局变量与局部变量。 Lua 中的变量默认都是全局变量,即使声明在语句块或函数里。全局变量一旦声明,在当前文件 中的任何地方都可访问。局部变量 local 相当于 Java 中的 private 变量,只能在声明的语句块中 使用。
| and | break | do | else |
|---|---|---|---|
| elseif | end | false | for |
| function | if | in | local |
| nil | not | or | repeat |
| return | then | true | until |
| while | goto |
运算符
其余基本运算符同其他语言相同
| 符号 | 作用 | 结果 |
|---|---|---|
| ^ | 乘幂 | A^2 输出结果 100 |
| / | 除法 | 5 / 2 输出结果 2.5 |
| // | 整除运算符(>=lua5.3) | 5//2 输出结果 2 |
| ~= | 不等于 | a~=b |
| and/or/not | 与/或/非 | A and/or/not B |
| .. | 字符串连接符 | "Hello ".."World"输出结果为 "Hello World" |
| # | 返回字符串或表的长度 | #"Hello" 返回 5 |
函数
[[--基本函数调用--]]
function add(a,b)
return a+b
end
-- 使用...代表可变参数,可以传多个变量、函数,可以获取其值分离到变量中。
function test(...)
local a,b,func=...
-- 一次性可以返回多个值,但是需要使用多个变量接收
return func(a,b),a-b,a*b,a/b
end
-- 函数也可作为方法的参数进行传递,并且可以调用
a,b,c,d=test(1,2,add) -- 3 -1 2 0.5
[[-- 匿名函数调用 --]]
f(3, 5, function (a, b)
return a - b;
end
);流程控制语句
if
-- false 与 nil 作为假,将 true 与非 nil 作为真,即使是 0 也是真。
-- if后判断条件可以使用括号扩起,也可以不使用
--
if a>0 then
print("num > 0")
elseif num == 0 then
print("num = 0")
else
print("num < 0")
end循环控制
a = 3
while a>0 do
print(a)
a = a - 1
end
repeat
print(a)
a = a - 1
until a <= 0
-- exp1 为循环起始值,exp2 为循环结束值,exp3 为循环步长。步长可省略不写,默认为 1
-- 如果 var 小于等于 exp2 的值,则继续循环,否则结束循环。
for var=exp1, exp2, exp3 do
循环体
endgoto
lua5.1中还不支持goto
for i = 1, 10 do
print("在标签跳转前: " .. i)
if i == 5 then
goto skip -- 当i等于5时跳转到标签skip
end
end
::skip:: -- 定义标签skip
print("跳转到标签skip后执行的代码")进阶语法
table
作为数组使用
-- 初始可以一次性赋多个值
cities = {"北京", "上海", "广州"}
-- 可以为指定位置赋值
cities[4] = "深圳"
-- table初始下标为1,访问0和未赋值下标得到的都是nil
print(cities[0])
-- 二维数组声明
arr = {}
for i=1, 3 do
-- 添加至arr的元素都是数组类型
arr[i] = {}
for j=1, 2 do
-- 为数组元素中的元素赋值
arr[i][j] = i*j
end
end作为map使用
emp = {name="张三", age=23, depart="销售部"}
-- 通过下标方式获取或添加元素
emp["gender"] = "男"
-- 点号方式操作
emp.office = "2nd floor"混合结构
Lua 允许将数组与 key-value 混合在同一个 table 中进行定义。key-value 不会占用数组的 数字索引值。
table操作函数
-- 下方所有对数组的操作,都是忽略map元素
-- 将指定的 table 数组元素进行字符串连接,连接从 start 索引位置到 end,无start和end,默认全部
table.concat (table, sep , start, end)
-- 返回指定 table 的数组中的从第 i 个元素到第 j 个元素值,无i,j,默认返回所有
table.unpack (table , i , j) -- lua版本>5.1
-- 指定的参数打包为一个 table 返回,其参数是可变的,可以传入多个
table.pack(...)-- lua版本>5.1
-- 返回指定 table 的数组中的最大索引值,不一定是数组包含元素的个数
table.maxn(table)
-- 在指定 table 的数组部分指定位置 pos 插入值为 value 的一个元素。其后的元素会被后移。pos 参数可选,默认为数组部分末尾
table.insert (table, pos, value)
-- 该函数用于删除并返回指定 table 中数组部分位于 pos 位置的元素。其后的元素会被前移。pos 参数可选,默认删除数组中的最后一个元素
table.remove (table [, pos])
-- 该函数用于对指定的 table 的数组元素进行升序排序,也可按照指定函数 fun(a,b)中指定的规则进行排序
-- 如果 arr 中的元素既有字符串又有数值型,那么对其进行排序会报错。
-- 如果数组元素中包含 nil,则排序会报错。
-- 降序排序
-- table.sort(cities, function(a,b)
-- return a<b
-- end
-- )
table.sort(table ,fun(a,b))迭代器
- ipairs(table):仅会迭代指定 table 中的数组元素。
- pairs(table):会迭代整个 table 元素,无论是数组元素,还是 key-value。
emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", "深圳"}
-- 遍历emp中的所有数组元素
for i, v in ipairs(emp) do
print(i, v)
end
-- 1 北京
-- 2 上海
-- 3 广州
-- 4 深圳
-- 遍历emp中的所有元素,先遍历完数组,再遍历key-value
for k, v in pairs(emp) do
print(k, v)
end
-- 1 北京
-- 2 上海
-- 3 广州
-- 4 深圳
-- depart 销售部
-- name 张三
-- age 23模块化
模块文件主要由 table 组成。在 table 中添加公共的变量、函数,最后文件返回该 table 即可。如果其它文件中需要使用该模块,只需通过 require 将该模块导入即可。
模块代码
-- 声明一个模块
rectangle = {}
-- 为模块添加一个变量
rectangle.pi = 3.14
-- 为模块添加函数(求周长)
function rectangle.perimeter(a,b)
return (a+b) * 2
end
-- 以匿名函数方式为模块添加一个函数(求面积)
rectangle.area = function(a, b)
return a*b;
end
-- ========== 定义与模块无关的内容 =============
-- 定义一个全局变量
goldenRatio = 0.618
-- 定义一个局部函数(求圆的面积)
local function circularArea(r)
return rectangle.pi * r * r
end
-- 定义一个全局函数(求矩形中最大圆的面积)
function maxCircularArea(a, b)
local r = math.min(a, b)
return circularArea(r)
end
return rectangle使用模块
-- 导入一个模块
rect = require "rectangle"
-- 访问模块的属性,调用模块的函数
print(rectangle.pi)
print(rectangle.perimeter(3, 5))
print(rectangle.area(3, 5))
-- 可以使用接受模块的参数作为别名访问模块内容
print(rect.pi)
print(rect.perimeter(3, 5))
print(rect.area(3, 5))
-- 访问模块中与模块无关的内容
print(goldenRatio)
print(maxCircularArea(3, 5))
print(circularArea(3)) -- 无法访问元表
每个table都可以有一个元表,类似“规则书”,当Lua在一个table上执行某些操作时,如果这个table自身没有直接定义如何进行该操作,Lua就会查看这个table的元表(如果有的话)来找出如何执行这个操作。
setmetatable(table,metatable)
将 metatable 指定为普通表 table 的元表。
-- 声明一个元表,元表本质上也是table
meta = {};
-- 将原始表与元表相关联
setmetatable(emp, meta)getmetatable(table)
获取指定普通表 table 的元表。
_ _ index
当用户在对 table 进行读取访问时,如果访问的数组索引或 key 不存在,那么系统就会自动调用元表的 _ _index元方法。
-- __index绑定的可以是函数
-- 有返回值的情况,访问不存在的下标将会得到返回值
meta.__index = function(tab, key)
return "通过【"..key.."】访问的值不存在"
end
-- 无返回值的情况,访问不存在的下标将会得到nil
meta.__index = function(tab, key)
print("通过【"..key.."】访问的值不存在")
end
-- __index绑定的可以是一个table,在原始表中若找不到,则会到元表指定的普通表中查找
-- 声明一个元表
meta = {};
-- 将原始表与元表相关联
setmetatable(emp, meta)
-- 再定义一个普通表
other = {}
other[5] = "天津"
other[6] = "西安"
-- 指定元表为另一个普通表
meta.__index = other_ _newindex
当用户为 table 中一个不存在的索引或 key 赋值时,就会自动调用元表的 _ _ newindex 元 方法。该重写的方法可以是一个函数,也可以是另一个表。
-- __newindex绑定的可以是函数
function meta.__newindex(tab, key, value)
print("新增的key为"..key..", value为"..value)
-- 将新增的key-value写入到原始表
rawset(tab, key, value)
end
-- __newindex绑定的可以是table
-- 定义一个普通表
other = {}
-- 元表指定的另一个普通表的作用是,暂存新增加的数据,则元表对应的表无法访问到新加的数据,只有other中可以访问到
meta.__newindex = other运算符重载
如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的 元方法
| 元方法 | 说明 | 元方法 | 说明 |
|---|---|---|---|
__add | 加法,+ | __sub | 减法,- |
__mul | 乘法,* | __div | 除法,/ |
__mod | 取模,% | __pow | 次幂,^ |
__unm | 取反,- | __idiv | 取整除法,// |
__band | 按位与,& | __bor | 按位或, |
__bxor | 按位异或,~ | __bnot | 按位非,~ |
__shl | 按位左移,<< | __shr | 按位右移,>> |
__concat | 字符串连接,.. | __len | 字符串长度,# |
__eq | 等于,== | __lt | 小于,< |
__le | 小于等于,<= |
emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", 12, "深圳"}
-- 声明一个元表
meta = {
-- 传入的参数是运算符两边的参数
__add = function(tab, num)
-- 遍历tab中的所有元素
for k, v in pairs(tab) do
-- 若value为数值类型,则做算术加法
if type(v) == "number" then
tab[k] = v + num
-- 若value为string,则做字符串拼接
elseif type(v) == "string" then
tab[k] = v..num
end
end
-- 返回变化过的table
return tab
end, -- 注意,这里必须要添加一个逗号
__tostring = function(tab)
str = ""
-- 字符串拼接
for k, v in pairs(empsum) do
str = str.." "..k..":"..v
end
return str
end
};
-- 将原始表与元表相关联
setmetatable(emp, meta)
-- 所有数字元素将会加上,所有字符元素将会拼接上5
empsum = emp + 5__toString
meta = {
__tostring = function(tab)
str = ""
-- 字符串拼接
for k, v in pairs(empsum) do
str = str.." "..k..":"..v
end
return str
end
};__call
将一个 table 以函数形式来使用时,系统会自动调用重写的_ _call 元方法。
emp = {"北京", name="张三", age=23, "上海", depart="销售部", 59, "广州", "深圳"}
-- 将原始表与匿名元表相关联
setmetatable(emp, {
__call = function(tab, num, str)
-- 遍历table
for k, v in pairs(tab) do
if type(v) == "number" then
tab[k] = v + num
elseif type(v) == "string" then
tab[k] = v..str
end
end
return tab
end
})
-- 以函数的方式调用元表
newemp = emp(5, "-hello")
for k, v in pairs(newemp) do
print(k..":"..v)
end面向对象
Lua 中没有类的概念,但通过 table、function 与元表可以模拟和构造出具有类这样功能的结构。
-- 创建一个类
Animal = {name = "no_name", age = 0}
-- 冒号中会自动包含一个self参数,表示当前对象本身,相当于this
function Animal:bark(voice)
print(self.name.."在"..voice.."叫")
end
-- 为该类添加一个无参构造器
function Animal:new()
-- 创建一个空表
a = {}
-- 为新表指定元表为当前基础类表,在新表(a)中找不到的key,会从self基础类表中查找
setmetatable(a, {__index=self})
-- 返回新表,相当于一个对象
return a
end
-- 为该类添加一个带参构造器(下面举例是参数为table的情况)
function Animal:new(obj)
-- 参数非空,则新表就是传入参数
local a = obj or {}
setmetatable(a, {__index=self})
-- 返回新表
return a
end
-- 调用时,使用冒号进行调用
animal = Animal:new()
-- 调用带参构造器,实参为一个匿名表
animal3 = Animal:new({type="老鼠"})协同线程与协同函数
协同线程
Lua 中有一种特殊的线程,称为 coroutine,协同线程,简称协程。其可以在运行时暂停执行,然后转去执行其它线程,然后还可返回再继续执行没有执行完毕的内容。在 Lua 中表示独立的执行线程。任意时刻只会有一个协程执行。
协同线程的类型为 thread,其启动、暂停、重启等,都需要通过函数来控制。
| 方法 | 描述 |
|---|---|
create(function) | 创建一个协同线程实例,即返回的是 thread 类型。参数是一个function。其需要通过 resume()来启动协同线程的执行 |
resume(thread, ...) | 启动指定的协同线程的执行,使其从开始处或前面挂起处开始执行。可以向 create()的内置函数传递相应的参数。如果内置函数具有返回值,resume()会全部接收并返回。 |
running() | 返回正在运行的协同线程实例,即 thread 类型值 |
yield() | 挂起协同线程,并将协同线程设置为挂起状态。resume()可从挂起处重启被挂起的协同线程 |
status(thread) | 查看协同线程的状态。状态有三种:运行态 running,挂起态 suspended,消亡态 dead |
close() | 关闭协同线程 |
wrap(function) | 创建一个协同函数,返回的是 function 类型。一旦调用该函数就会创建并执行一个协同线程实例 |
-- 创建一个协同线程实例
crt = coroutine.create(
function (a, b)
print(a, b , a+b)
-- 获取正在运行的协同线程实例,thread类型
tr = coroutine.running(); -- thread: 00D1EB40
print(tr)
-- 查看协同线程实例的类型
print(type(tr)) -- thread
-- 查看协同线程实例的状态
print(coroutine.status(crt)) -- running
-- 将当前协同线程实例挂起,同时携带两个返回值a*b与a/b
coroutine.yield(a*b, a/b)
-- 返回两个值
return a+b, a-b
end
)
-- 启动协同线程,返回三个值
-- 第一个返回值为协同线程启动成功状态,后面的返回值为内置函数的返回值
success, result1, result2 = coroutine.resume(crt, 12, 3)--调用resume,将从头(或上一个yield)开始执行到结束(或下一个yield)
-- true 36 4
print(success, result1, result2)
-- 查看协同线程实例的状态
print("main-"..coroutine.status(crt)) -- main-suspended
-- 继续执行协同线程
success, result1, result2 = coroutine.resume(crt)
-- true 15 9
print(success, result1, result2)
-- 查看协同线程实例的状态
print("main-"..coroutine.status(crt)) --main-dead,协程执行结束协同函数
协同线程可以单独创建执行,也可以通过协同函数的调用启动执行。使用 coroutine 的 wrap()函数创建的就是协同函数,其类型为 function,只不过,协同函数的调用会启动其内置的协同线程。
-- 创建一个协同函数
cf = coroutine.wrap(
function (a, b)
print(a, b)
-- 获取当前协同函数创建的协同线程
tr = coroutine.running()
print("tr的类型为:".. type(tr))
-- 挂起当前的协同线程
coroutine.yield(a+1, b+1)
print("又重新返回到了协同线程")
return a+b, a*b
end
)
-- 调用协同函数,启动协同线程
result1, result2 = cf(3, 5)
print(result1, result2)
print("cf的类型为:".. type(cf))
-- 重启挂起的协同线程
result1, result2 = cf(3, 5)
print(result1, result2)文件IO
常用静态函数
| 常用静态函数 | 格式 | 解析 |
|---|---|---|
io.open() | io.open(filename [, mode]) | 以指定模式打开指定文件,返回要打开文件的句柄,就是一个对象。其中模式 mode 有三种,但同时还可配合两个符号使用:<br>• r:只读,默认模式<br>• w:只写,写入内容会覆盖文件原有内容<br>• a:只写,以追加方式写入内容<br>• +:增加符,在 r+、w+、a+ 均变为了读写<br>• b:二进制表示符。如果要操作的文件为二进制文件,则需要变为 rb、wb、ab。 |
io.input() | io.input(file) | 指定要读取的文件。 |
io.output() | io.output(file) | 指定要写入的文件。 |
io.read() | io.read([format]) | 以指定格式读取 io.input() 中指定的输入文件。其中 format 格式有:<br>• *l:从当前位置的下一个位置开始读取整个行,默认格式<br>• *n:读取下一个数字,其将作为浮点数或整数<br>• *a:从当前位置的下一个位置开始读取整个文件<br>• number:这是一个数字,表示要读取的字符的个数 |
io.write() | io.write(data) | 将指定的数据 data 写入到 io.output() 中指定的输出文件。 |
-- 以只读方式打开一个文件
file = io.open("info.properties", "r")
-- 指定要读取的文件为file
io.input(file)
-- 读取一行数据
line = io.read("*l")
-- 只要有数据,就一直读下去
while line ~= nil do
print(line)
line = io.read("*l")
end
-- 关闭文件
io.close(file)
-- 以追加方式打开一个文件
file = io.open("info.properties", "a")
-- 指定要写入的文件为file
io.output(file)
-- 写入一行数据
io.write("\ngender=male")
io.close(file)常用实例函数
file:read()
- 这里的
file使用的是io.open()函数返回的file,其实际就是 Lua 中的一个对象。其用法与io.read()的相同。
- 这里的
file:write()
- 用法与
io.write()的相同。
- 用法与
file:seek()
- 格式:
file:seek([whence [, offset]]) - 解析: 该函数用于获取或设置文件读写指针的当前位置。位置从 1 开始计数,除文件最后一行外,每行都有行结束符,其会占两个字符位置。位置 0 表示文件第一个位置的前面位置。当
seek()为无参时会返回读写指针的当前位置。参数whence的值有三种,表示将指针定位的不同位置。而offset则表示相对于whence指定位置的偏移量,offset的默认值为 0,为正表示向后偏移,为负表示向前偏移。set:表示将指针定位到文件开头处,即 0 位置处cur:表示指针保持当前位置不变,默认值end:表示将指针定位到文件结尾处。
- 格式:
-- 以只读方式打开一个文件
file = io.open("info.properties", "r")
-- 读取一行数据
line = file:read("*l")
-- 只要有数据,就一直读下去
while line ~= nil do
print(line)
line = file:read("*l")
end
-- 关闭文件
file:close()
-- 以追加方式打开一个文件
file = io.open("info.properties", "a")
-- 写入一行数据
file:write("\nlevel=P7")
file:close()