Skip to content

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 变量,只能在声明的语句块中 使用。
andbreakdoelse
elseifendfalsefor
functionifinlocal
nilnotorrepeat
returnthentrueuntil
whilegoto

运算符

其余基本运算符同其他语言相同

符号作用结果
^乘幂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

函数

lua
[[--基本函数调用--]]
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

lua
-- 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

循环控制

lua
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
   循环体
end

goto

lua5.1中还不支持goto

lua
for i = 1, 10 do
    print("在标签跳转前: " .. i)
    if i == 5 then
        goto skip -- 当i等于5时跳转到标签skip
    end
end

::skip:: -- 定义标签skip

print("跳转到标签skip后执行的代码")

进阶语法

table

作为数组使用

lua
-- 初始可以一次性赋多个值
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使用

lua
emp = {name="张三", age=23, depart="销售部"}

-- 通过下标方式获取或添加元素
emp["gender"] = "男"

-- 点号方式操作
emp.office = "2nd floor"

混合结构

Lua 允许将数组与 key-value 混合在同一个 table 中进行定义。key-value 不会占用数组的 数字索引值。

table操作函数

lua
-- 下方所有对数组的操作,都是忽略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。
lua
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 将该模块导入即可。

模块代码

lua
-- 声明一个模块
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

使用模块

lua
-- 导入一个模块
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 的元表。

lua
-- 声明一个元表,元表本质上也是table
meta = {};
-- 将原始表与元表相关联
setmetatable(emp, meta)

getmetatable(table)

获取指定普通表 table 的元表。

_ _ index

当用户在对 table 进行读取访问时,如果访问的数组索引或 key 不存在,那么系统就会自动调用元表的 _ _index元方法。

lua
-- __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 元 方法。该重写的方法可以是一个函数,也可以是另一个表。

lua
-- __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小于等于,<=
lua
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

lua
meta = {
	__tostring = function(tab)
		str = ""
		-- 字符串拼接
		for k, v in pairs(empsum) do
			str = str.." "..k..":"..v
		end

		return str
	end
};

__call

将一个 table 以函数形式来使用时,系统会自动调用重写的_ _call 元方法。

lua
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 与元表可以模拟和构造出具有类这样功能的结构。

lua
-- 创建一个类
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 类型。一旦调用该函数就会创建并执行一个协同线程实例
lua
-- 创建一个协同线程实例
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,只不过,协同函数的调用会启动其内置的协同线程。

lua
-- 创建一个协同函数
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:二进制表示符。如果要操作的文件为二进制文件,则需要变为 rbwbab
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() 中指定的输出文件。
lua
-- 以只读方式打开一个文件
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:表示将指针定位到文件结尾处。
lua
-- 以只读方式打开一个文件
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()