Lua 基础语法笔记

Lua 是一种轻量小巧的脚本语言, 使用标准C语言编写并以源代码形式开放.
其设计目的是为了嵌入应用程序中, 从而为应用程序提供灵活的扩展和定制功能.
Lua 是巴西里约热内卢天主教大学 (Pontifical Catholic University of Rio de Janeiro) 里的一个研究小组于 1993 年开发的, 该小组成员有: Roberto Ierusalimschy, Waldemar Celes, Luiz Henrique de Figueiredo

注释

单行注释

Lua 中的单行注释用 -- 表示

1
2
-- Hello World
-- Hello Lua

多行注释

Lua 中的多行注释由 --[[ 开始, 以 --]] 结束

1
2
3
4
--[[
Hello World
Hello Lua
--]]

关键字

Lua中一共有 22 个关键字

  • and
    布尔运算和
  • break
    跳出循环
  • do
    代码块开始标记
  • else
    分支语句表示默认分支
  • elseif
    分支语句表示另一个条件分支
  • end
    代码块结束标记
  • false
    条件假
  • for
    循环语句关键字
  • function
    函数声明关键字
  • if
    分支语句关键字
  • in
    迭代关键字
  • local
    局部变量声明关键字
  • nil
    空类型声明关键字
  • not
    布尔运算非
  • or
    布尔运算或
  • repeat
    直到循环声明关键字
  • return
    函数返回关键字
  • then
    分支语句关键字
  • true
    条件真
  • until
    直到循环声明关键字
  • while
    当循环声明关键字
  • goto
    跳转语句关键字

变量类型

Lua 中的变量分为全局变量和局部变量, 声明变量时未加 loacl 关键字的变量为全局变量

全局变量的首字母通常大写

1
2
Var = nil -- 全局变量
local var = nil -- 局部变量

数据类型

Lua 中由8种数据类型, 分别是

  • nil
  • boolean
  • number
  • string
  • table
  • function
  • userdata
  • thread

nil 类型

nil类型的变量只有一种值, 即nil, 表示空

1
2
3
local var = nil
print(type(var)) -- output: nil
print(type(var) == 'nil') -- output: true

boolean 类型

Lua 中所有数据类型都可视作 boolean 类型, false 和 nil 看作是 false,其他的都为 true

1
2
3
local var = true -- boolean   false 和 nil 看作是 false,其他的都为 true
var = false
print(type(var)) -- output: boolean

number 类型

Lua 中 的数字默认可分为 64 位整数和 64 位浮点数

1
2
3
4
5
local var = 123
print(type(var)) -- output: number
var = 0.3
print(type(var)) -- output: number
print(0.1 + 0.2) -- output: 0.3

string 类型

Lua 中声明 string 类型变量由三种方式, 声明单行 string 可用 '" , 声明多行 string 可用 [[ 开始, ]] 结束

1
2
3
4
5
6
local var = 'abc'
var = "abc"
var = [[
abc
]]
print(type(var)) -- output: string
注意 在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字, 字符串连接使用的是 .. , 使用 # 来获取字符串的长度
1
2
3
4
print("2" + "6") -- output: 8
print('2' .. '6') -- output: 26
local var = '123'
print(#var) -- output: 3

funcyion 类型

Lua 的函数类型变量分为有名函数和匿名函数

匿名函数通常用作给函数传参来使用

1
2
3
local var = function() end
function var() end
print(type(var)) -- output: function
1
print(type(function() end))  -- output: function

table 类型

Lua 中唯一的一种内置的数据结构就是表, 由许多 k, v 键值对组成

1
2
local var = {}
print(type(var)) -- output: table

userdata 类型

Lua 中的 userdata 类型变量表示任意存储在变量中的C数据结构

thread 类型

Lua 中的 thread 类型变量表示执行的独立线路,用于执行协同程序

函数

Lua 中的函数是一种数据类型, 具体声明结构为

1
2
3
4
5
6
7
function fun_name(arg1, arg2, arg3, ...)
-- body
local arg={...} -- 将可变参数转换为表
local len = select("#",...) -- 获取可变参数长度
local arg1 = select(1, ...) --用于返回从起点 n 开始到结束位置的所有参数列表
return '1', 2, arg3
end

函数调用形式为

1
local r1, r2, r3 = fun('arg1', 'arg2', function () end, '1') -- 函数调用

lua 中 . 和 : 的区别
首先在lua中使用 : 定义的函数会自动传入一个名为self的变量,这个变量是隐含的,self同c++中的this一样,表示当前对象的指针, 而 . 定义的函数中没有self

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Class = {}
Class.__index = Class

function Class.new(x,y)
local cls = {}
setmetatable(cls, Class)
cls.x = x
cls.y = y
return cls
end
function Class:test()
-- 等价于
-- function Class.test(self)
print(self.x,self.y)
end

object = Class.new(10,20)

object:test() -- 等价于
object.test(object)

Lua 中声明表时可对表进行初始化

1
local var = {index1 = 'index1', index2 = 2}

填充表时可用.运算符或[]运算符

1
2
table.index1 = 1
table['index2'] = 'index2'

删除表中的值时直接将该元素置为 nil 即可

1
table.index2 = nil

访问表中元素时可用索引去取值, 或者使用^遍历的方法访问表

1
2
3
print(table['index1'])
print(table.index2)
-- 遍历的方法见循环语句

数组

当声明表并对其初始化时不填写索引或之后用 [数字] 的方式填表中的充值, 那么此时这个表可视为数组

Lua 的默认索引是从 1 开始的

1
2
3
local array = {1, 2, 3}
array[4] = 4
print(array[1]) -- output: 1

可使用^遍历的方法访问数组

标签

Lua 中使用 ::tag_name::的形式声明标签, 通常标签和 goto 关键字搭配使用

赋值语句

Lua 可以对多个变量同时赋值, 例如local a, b = 1,2

当变量个数和值的个数不一致时,Lua会以变量个数为基础采取以下策略:

  • 变量个数 > 值的个数 按变量个数补足nil
  • 变量个数 < 值的个数 多余的值会被忽略

分支语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if condition then
-- body
end

if condition then
-- body
else
-- body
end

if condition then
-- body
elseif condition then
-- body
else
-- body
end

循环语句

循环语句中可用 break 关键字跳出当前循环, 或搭配标签和 goto关键字实现 continue

1
2
3
4
while condition do
-- body
break -- 跳出循环
end
1
2
3
4
:: continue::while condition do
-- body
goto continue
end

当型循环

1
2
3
while condition do
-- body
end

直到型循环

1
2
3
:: continue::repeat
-- body
until(condition)

for循环

普通 for 循环

1
2
3
4
for i = 1, 5, 2 do
-- 1 从 1(exp1) 变化到 5(exp2),每次变化以 2(exp3) 为步长递增 1,并执行一次 "执行体". exp3 是可选的,如果不指定,默认为1
-- body
end

遍历表

1
2
3
for k, v in pairs(table) do
print(k, v)
end

遍历数组

1
2
3
for i, v in ipairs(array) do
print(i, v)
end

运算符

Lua 中有以下运算符

  • + 加法

  • - 减法

  • * 乘法

  • / 除法

  • % 取余

  • ^ 乘幂

  • - 负号

  • // 整除运算符(>=lua5.3)

  • == 等于

  • ~= 不等于

  • > 大于

  • < 小于

  • >= 大于等于

  • <= 小于等于

  • and 逻辑与

  • or 逻辑或

  • not 逻辑非

  • .. 连接两个字符串

  • # 一元运算符,返回字符串或表的长度

运算优先级由高到低排列为

优先级 运算符
1 ^
2 not, - (unary)
3 *, /, %
4 +, -
5 ..
6 <, >, <=, >=, ~=, ==
7 and
8 or

模块

Lua 中引如其他模块使用require("module_name")函数, 此时当前程序内会引入一个其他模块名称的表

1
2
3
4
require("module")
print(module.constant)
local m = require("module") -- 模块别名
print(m.constant)
1
2
3
-- 文件名为 module.lua
module = {} -- 定义一个名为 module 的模块
module.constant = "constant" -- 将一个数据添加进 module 表

元表

元表用于为表定义一些行为, 为表添加元表的方法为

1
2
3
4
5
6
7
8
9
10
11
12
local a_table = {}                  -- 普通表
local metatable = {} -- 元表
setmetatable(a_table,metatable) -- 把 mymetatable 设为 mytable 的元表

metatable['__index'] = function(target_table, key) -- 添加元表函数
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
print(a_table.key1,a_table.key2) -- output: value1 metatablevalue
函数名称 函数作用
__index 当通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键. 如果__index包含一个表,Lua会在表格中查找相应的键
__newindex 当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作
__call 在把普通表视作函数并调用时调用
__tostring 修改表的输出行为
__add 对应的运算符 ‘+’
__sub 对应的运算符 ‘-‘
__mul 对应的运算符 ‘*’
__div 对应的运算符 ‘/‘
__mod 对应的运算符 ‘%’
__unm 对应的运算符 ‘-‘
__concat 对应的运算符 ‘..’
__eq 对应的运算符 ‘==’
__lt 对应的运算符 ‘<’
__le 对应的运算符 ‘<=’

协同程序(coroutine)

函数 作用
coroutine.create(func) 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
coroutine.resume() 启动 coroutine,和 create 配合使用
coroutine.yield() 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果
coroutine.status() 查看 coroutine 的状态(coroutine 的状态有三种:dead,suspended,running)
coroutine.wrap() 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复
coroutine.running() 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local co = coroutine.create(
function(arg)
for i = 1, 3 do
print(coroutine.running())
coroutine.yield(arg)
end

end
)
print('status ' .. coroutine.status(co))

for i = 1, 4 do
local is_success, r = coroutine.resume(co, 'arg')
print(r)
print('status ' .. coroutine.status(co))
end
1
2
3
4
5
6
7
local wr = coroutine.wrap(
function(arg)
print(arg);
end
)

wr(1)

I/O

IO模式

参数 作用
r 读模式
w 写模式
a 追加模式
r+ 更新模式,所有之前的数据都保留
w+ 更新模式,所有之前的数据都删除
a+ 追加更新模式,所有之前的数据都保留,只允许在文件尾部做写入
rb 读模式(二进制方式)
wb 写模式(二进制方式)
ab 追加模式(二进制方式)
r+b 更新模式,所有之前的数据都保留(二进制方式)
w+b 更新模式,所有之前的数据都删除(二进制方式)
a+b 追加更新模式,所有之前的数据都保留,只允许在文件尾部做写入(二进制方式)

简单模式

1
2
3
4
5
6
7
8
9
file = io.open("test.lua", "a") -- 以附加的方式打开只写文件
io.output(file) -- 设置默认输出文件为 test.lua
io.write("-- test.lua 文件末尾注释") -- 在文件最后一行添加 Lua 注释
io.close(file) -- 关闭打开的文件

local file = io.open ('module.lua', 'r') -- 以只读方式打开文件
io.input(file) -- 设置默认输入文件为 test.lua
print(io.read()) -- 输出文件第一行
io.close(file)

io.read()参数

参数 作用
“*n” 读取一个数字并返回它
“*a” 从当前位置读取整个文件
“*l”(默认) 读取下一行,在文件尾 (EOF) 处返回 nil
number 返回一个指定字符个数的字符串,或在 EOF 时返回

其他的 io 函数

函数 作用
io.tmpfile() 返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
io.type(file) 检测obj是否一个可用的文件句柄
io.flush() 向文件写入缓冲中的所有数据
io.lines(optional file name) 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回 nil,但不关闭文件

完全模式

1
2
3
4
5
6
7
file = io.open("test.lua", "r") -- 以只读方式打开文件
print(file:read()) -- 输出文件第一行
file:close() -- 关闭打开的文件

file = io.open("test.lua", "a") -- 以附加的方式打开只写文件
file:write("--test") -- 在文件最后一行添加 Lua 注释
file:close() -- 关闭打开的文件

file:read()参数

参数 作用
“*n” 读取一个数字,根据 Lua 的转换文法返回浮点数或整数
“*a” 从当前位置开始读取整个文件
“*l” 读取一行并忽略行结束标记 (默认)
“*L” 读取一行并保留行结束标记

其他函数

函数 作用
file:seek(optional whence, optional offset) 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息
file:flush() 向文件写入缓冲中的所有数据
io.lines(optional file name) 打开指定的文件 filename 为读模式并返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回 nil,并自动关闭文件. 若不带参数时io.lines() <=> io.input():lines(); 读取默认输入设备的内容,但结束时不关闭文件

file:seek()参数:

  1. whence 值可以是:
  • “set”: 从文件头开始
  • “cur”: 从当前位置开始[默认]
  • “end”: 从文件尾开始
  1. offset 偏移量(默认为0)

断言

1
assert(type(true) == "number", "true 不是一个数字")

错误

error()

error(messahe, level) 函数可终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部

参数名称 意义
messaage 错误信息
level 指示获得错误的位置
Level=1[默认]: 为调用error位置(文件+行号)
Level=2: 指出哪个调用error的函数的函数
Level=0: 不添加错误位置信息

错误处理

pcall()

pcall接收一个函数和要传递给后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo
pcall以一种”保护模式”来调用第一个参数,因此pcall可以捕获函数执行中的任何错误
通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置. 但pcall返回时,它已经销毁了调用桟的部分内容

1
2
3
4
5
6
7
if pcall(function (x, y, z) error('ERROR') end, 1, 2, 3) then
print('SUCCESS')
-- 没有错误
else
print('ERROR')
-- 一些错误
end

xpcall()

xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展开(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了
debug库提供了两个通用的错误处理函数:

  • debug.debug(): 提供一个Lua提示符,让用户来检查错误的原因
  • debug.traceback(): 根据调用桟来构建一个扩展的错误消息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    local myfunction = function (x, y)
    x = y / nil
    end

    local myerrorhandler = function ( err )
    print( "ERROR:", err )
    end

    local status = xpcall(myfunction, myerrorhandler, 1, 2)
    print(status)