Lua
# Lua笔记
lua5.3参考手册: luatos.com (opens new window)
# 简介
描述
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放; 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
特性
- 轻量级
- 可扩展
- 其它特性:支持面向过程、自动内存管理、语言内置模式匹配、支持面向对象
应用场景
- 游戏开发
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
- 安全系统,如入侵检测系统
Linux环境安装
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxf lua-5.3.0.tar.gz
cd lua-5.3.0
make linux test
make install
2
3
4
5
MacOS环境安装
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxf lua-5.3.0.tar.gz
cd lua-5.3.0
make macosx test
make install
2
3
4
5
Window环境安装
- Github下载安装包
- 双击安装后即可在该环境下编写 Lua 程序并运行
# 基础语法
# 语法
lua代码程序后缀 .lua
打印
print("hello")
注释
-- 单行注释
--[[
多行注释
多行注释
--]]
2
3
4
5
6
变量命名
Lua 标示符用于定义一个变量,函数获取其他用户定义的项。
标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
Lua 不允许使用特殊字符如@, $, 和 %
来定义标示符。
Lua 是一个区分大小写的编程语言。
在默认情况下,变量总是认为是全局的。
PS:如果你想删除一个全局变量,只需要将变量赋值为nil。
关键词
一般约定,以下划线开头连接一串大写字母的名字(比如_VERSION
)被保留用于 Lua 内部全局变量。
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while | goto |
作用域
- 全局变量
- Lua 中的变量默认全局变量,无论语句块或是函数里
- 变量默认值均为nil
- 局部变量
- 用 local 显式声明为局部变量,局部变量只在被声明的那个代码块内有效
- 变量默认值均为nil
- 代码块:指的是一个控制结构内,一个函数体,或者一个chunk(变量被声明的那个文件或者文本串)
- 局部变量的作用域为从声明位置开始到所在语句块结束
- 表中域
尽可能使用局部变量,有两个好处
- 避免命名冲突
- 访问局部变量的速度比全局变量更快
- 原因是local变量是存放在lua的堆栈里面的是array操作,而全局变量是存放在_G中的table中,效率不及堆栈
数据类型
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 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
实例
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
2
3
4
5
6
7
# 基本变量
# nil
nil 作比较时应该加上双引号 ": type(X) 实质是返回的 "nil" 字符串,是一个 string 类型
> type(X)
nil
> type(X)==nil
false
> type(X)=="nil"
true
>
2
3
4
5
6
7
对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉
tab1 = { key1 = "val1", key2 = "val2", "val3" }
for k, v in pairs(tab1) do
print(k .. " - " .. v)
end
tab1.key1 = nil
for k, v in pairs(tab1) do
print(k .. " - " .. v)
end
2
3
4
5
6
7
8
9
# number
Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true
Lua 默认只有一种 number 类型 -- double(双精度)类型(默认类型可以修改 luaconf.h 里的定义)
print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1))
print(type(0.2e-1))
print(type(7.8263692594256e-06))
-- 字符串转number
print(tonumber("3333")) -- 3333
print(tonumber("aaa")) -- nil
2
3
4
5
6
7
8
9
大小端
对于一个由2个字节组成的16位整数,在内存中存储这两个字节有两种方法:
- 小端(little-endian)字节序: 低序字节存储在起始地址
- 大端(big-endian)字节序: 高序字节存储在起始地址。又称为网络序
uint32_t n = 0x01020304
# 大端顺序
0x01, 0x02, 0x03, 0x04
# 小端顺序
0x04, 0x03, 0x02, 0x01
2
3
4
5
6
7
不同CPU型号大小端模式不同, 判断大小端方式
#include <stdio.h>
// 共用体中很重要的一点:a和b都是从u1的低地址开始存放的。
// 假设u1所在的4字节地址分别是:0、1、2、3的话,那么a自然就是0、1、2、3;
// b所在的地址是0而不是3.
union myunion
{
int a;
char b;
};
// 如果是小端模式则返回1,小端模式则返回0
int is_little_endian(void)
{
union myunion u1;
u1.a = 0x12345678; // 地址0的那个字节内是0x78(小端)或者0x12(大端)
if(0x78 == u1.b)
return 1;
else if(0x12 == u1.b)
return 0;
}
int is_little_endian2(void)
{
int a = 0x12345678;
char b = *((char *)(&a)); // 指针方式其实就是共用体的本质
if(0x78 == b)
return 1;
else if(0x12 == b)
return 0;
}
int main(void)
{
int i = is_little_endian2();
//int i = is_little_endian();
if (i == 1)
{
printf("小端模式\n");
}
else
{
printf("大端模式\n");
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Lua中对二进制数据打包、解析
local data = string.pack("<L", 0x01020304)
print(data:byte(1)) -- 4
print(data:byte(2)) -- 3
print(data:byte(3)) -- 2
print(data:byte(4)) -- 1
local str = string.unpack("<L", data)
print(str) -- 16909060
2
3
4
5
6
7
# 字符串
字符串由一对双引号或单引号来表示,也可以用 2 个方括号 "[[]]" 来表示"一块"字符串 lua中字符串可以存储任何Byte值 字符串下标从1开始,反序下标从-1开始
string1 = "this is string1"
string2 = 'this is string2'
html = [[
<html>
<head></head>
<body>
<a href="http://www.runoob.com/">菜鸟教程</a>
</body>
</html>
]]
print(html)
print(str:byte(15)) -- 49
print(str:byte(-1)) -- 49
2
3
4
5
6
7
8
9
10
11
12
13
14
在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字
> print("2" + 6)
8.0
> print("2" + "6")
8.0
2
3
4
字符串连接使用 ..
> print("a" .. 'b')
ab
> print(157 .. 428)
157428
>
2
3
4
5
使用 # 来计算字符串的长度,放在字符串前面
> len = "www.runoob.com"
> print(#len)
14
> print(#"www.runoob.com")
14
>
2
3
4
5
6
语法糖
local str = "123"
print(string.byte(str, 1)) -- 49
print(str:byte(1)) -- 49
2
3
内建函数
序号 | 方法 & 用途 |
---|---|
string.upper | string.upper(argument) : 字符串全部转为大写字母。 |
string.lower | string.lower(argument): 字符串全部转为小写字母。 |
string.gsub | **string.gsub(mainString,findString,replaceString,num)**在字符串中替换。mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:> string.gsub("aaaa","a","z",3); zzza 3 |
string.find | string.find (str, substr, [init, [plain]]) 在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用使用正则模式匹配。以下实例查找字符串 "Lua" 的起始索引和结束索引位置:> string.find("Hello Lua user", "Lua", 1) 7 9 |
string.reverse | string.reverse(arg) 字符串反转> string.reverse("Lua") auL |
string.forma | string.format(...) 返回一个类似printf的格式化字符串> string.format("the value is:%d",4) the value is:4 |
string.char/string.byte | string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。> string.char(97,98,99,100) abcd > string.byte("ABCD",4) 68 > string.byte("ABCD") 65 > |
string.len | string.len(arg) 计算字符串长度。string.len("abc") 3 |
string.rep | string.rep(string, n) 返回字符串string的n个拷贝> string.rep("abcd",2) abcdabcd |
string.gmatch | string.gmatch(str, pattern) 返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end Hello Lua user |
string.match | string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。> = string.match("I have 2 questions for you.", "%d+ %a+") 2 questions > = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)")) 2, "questions" |
string.sub | string.sub(): 用于截取字符串string.sub(s, i [, j]) 。s:要截取的字符串;i:截取开始位置;j:截取结束位置,默认为 -1,最后一个字符。 |
# table
table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表;也可以在表里添加任意数据,直接初始化表
-- 创建一个空的 table
local tbl1 = {}
-- 直接初始表
local tbl2 = {"apple", "pear", "orange", "grape"}
-- 构造键值对
local tbl3 = {a=11, b="3", c=function() print("123") end, [",;"]=123 }
local tbl4 = {[1]=1,2,3,[4]=4,5}
2
3
4
5
6
7
8
9
对 table 的索引使用方括号 []。Lua 也提供了 . 操作。
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
2
3
table实质为"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
print(k .. " : " .. v)
end
--[[
key : value
10 : 33
]]
2
3
4
5
6
7
8
9
10
11
12
13
table默认初始索引一般以 1 开始,没有固定长度大小,随数据集新增而自动增长,未初始的数据返回nil ```lua tb = {1,2,3,4} print(tb[0]) -- nil print(tb[1]) -- 1 tb[0] = "11" print(tb[0]) -- 11 print(tb[4]) -- nil
全局变量统一存储在全局表(`_G`)中
```Lua
tb = {1,2,3,4}
print(_G) -- table: 0x24ffb80
print(_G.tb[1]) -- 1
print(_G.table.insert) -- function: 0x41d420
2
3
4
5
6
7
迭代器:pairs/ipairs
- pairs: 用于迭代table所有数据 返回下标以及值
- ipairs:用于返回int类型下标的数据,遇到不连续的下标时终止循环
- 内部同步next(table,index)函数实现
local t = {[0]="a", [4]=4, 5, [6]="b"}
for index,value in pairs(t) do
print(index,value)
end
--[[
1 5
0 a
6 b
4 4
]]
local t = {[1]="a", [2]="b", [3]="c",[4]="d", [6]="e"}
for i,j in ipairs(t) do
print(i, j)
end
--[[
1 a
2 b
3 c
4 d
]]
local t = {[1]="a", [2]="b", [3]="c",[4]="d", [6]="e"}
print(next(t)) -- 1 a
print(next(t,4)) -- 6 e
print(next(t,6)) -- nil
print(next({})) -- nil
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# function
Lua 编程语言函数定义格式如下:
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
2
3
4
- optional_function_scope: 该参数是可选的指定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
- function_name: 指定函数名称。
- argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
- function_body: 函数体,函数中需要执行的代码语句块。
- result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))
--[[
120
120
]]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function 可以以匿名函数(anonymous function)的方式通过参数传递
function testFun(tab,fun)
for k ,v in pairs(tab) do
print(fun(k,v));
end
end
tab={key1="val1",key2="val2"};
testFun(tab,
function(key,val)--匿名函数
return key.."="..val;
end
--[[
key1=val1
key2=val2
]]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。在return后列出要返回的值的列表即可返回多值
> s, e = string.find("www.runoob.com", "runoob")
> print(s, e)
-- 5 10
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5}))
-- 23 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ...
表示函数有可变的参数。
function average(...)
result = 0
local arg={...} --> arg 为一个表,局部变量
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. #arg .. " 个数")
return result/#arg
end
print("平均值为",average(10,5,3,4,5,6))
--[[
总共传入 6 个数
平均值为 5.5
]]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我们也可以通过select("#",...)
来获取可变参数的数量:
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. select("#",...) .. " 个数")
return result/select("#",...)
end
print("平均值为",average(10,5,3,4,5,6))
--[[
总共传入 6 个数
平均值为 5.5
]]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
固定参数必须放在变长参数之前
function fwrite(fmt, ...) ---> 固定的参数fmt
return io.write(string.format(fmt, ...))
end
fwrite("runoob\n") --->fmt = "runoob", 没有变长参数。
fwrite("%d%d\n", 1, 2) --->fmt = "%d%d", 变长参数为 1 和 2
--[[
runoob
12
]]
2
3
4
5
6
7
8
9
10
11
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …) 或者 select(n, …)
- select('#', …) 返回可变参数的长度。
- select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。
- 调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。
function f(...)
a = select(3,...) -->从第三个位置开始,变量 a 对应右边变量列表的第一个参数
print (a)
print (select(3,...)) -->打印所有列表参数
end
f(0,1,2,3,4,5)
--[[
2
2 3 4 5
]]
2
3
4
5
6
7
8
9
10
11
12
# thread
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
# userdata
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
# 循环
# for
重复执行指定语句,重复次数可在 for 语句中控制。
-- 数值for循环
-- for的三个表达式在循环开始前一次性求值,以后不再进行求值。
-- var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 **"执行体"**。
-- exp3 是可选的,如果不指定,默认为1。
for var=exp1,exp2,exp3 do
-- <执行体>
end
function f(x)
print("function")
return x*2
end
for i=1,f(5) do
print(i)
end
-- 泛型for循环
-- i是数组索引值,v是对应索引的数组元素值。
-- ipairs是Lua提供的一个迭代器函数,用来迭代数组。
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# while
在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
# repeat...until
重复执行循环,直到 指定的条件为真时为止
# IF判断
Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其他指定代码。
-- 示例1[ 0 为 true ]
if(0)
then
print("0 为 true")
end
-- 示例2 [ 定义变量 --]
a = 100;
--[ 检查条件 --]
if( a < 20 )
then
--[ if 条件为 true 时执行该语句块 --]
print("a 小于 20" )
else
--[ if 条件为 false 时执行该语句块 --]
print("a 大于 20" )
end
print("a 的值为 :", a)
local a = nil
local b = 0
print(a or b) -- 0
print(a and b) -- nil
print(not a) -- true
print(not b) -- false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 运算符
算数运算符
+ | 加法 | A + B 输出结果 30 |
---|---|---|
- | 减法 | A - B 输出结果 -10 |
* | 乘法 | A * B 输出结果 200 |
/ | 除法 | B / A 输出结果 2 |
% | 取余 | B % A 输出结果 0 |
^ | 乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果 -10 |
// | 整除运算符(>=lua5.3) | 5//2 输出结果 2 |
关系运算符
== | 等于,检测两个值是否相等,相等返回 true,否则返回 false | (A == B) 为 false。 |
---|---|---|
~= | 不等于,检测两个值是否相等,不相等返回 true,否则返回 false | (A ~= B) 为 true。 |
> | 大于,如果左边的值大于右边的值,返回 true,否则返回 false | (A > B) 为 false。 |
< | 小于,如果左边的值大于右边的值,返回 false,否则返回 true | (A < B) 为 true。 |
>= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false | (A >= B) 返回 false。 |
<= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false | (A <= B) 返回 true。 |
逻辑运算符
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
---|---|---|
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
others
.. | 连接两个字符串 | a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。 |
---|---|---|
# | 一元运算符,返回字符串或表的长度。 | #"Hello" 返回 5 |
优先级
-- 从高到低
^
not - (unary)
* / %
+ -
..
< > <= >= ~= ==
and
or
-- 除了 ^ 和 .. 外所有的二元运算符都是左连接的。
a+i < b/2+1 -- (a+i) < ((b/2)+1)
5+x^2*8 -- 5+((x^2)*8)
a < y and y <= z -- (a < y) and (y <= z)
-x^2 -- -(x^2)
x^y^z -- x^(y^z)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 语法补充
多文件调用
关键字:require
- 运行指定文件,且被调用文件指挥运行一次
- 末尾不带lua,自动匹配lua文件
- 目录层级使用”.“分隔
- 基本路径:
package.path
示例:
-- \path\txt_1.lua
print("txt_1 runtime!")
-- txt_2.lua
require("path.txt_1)
-- 设置默认引入包路径
package.path = package.path..";./lua/?.lua"
-- 封装包
-- Person.lua
local person = {}
function person.run()
print("run!")
end
-- app.lua
local person = require("Person")
person.run()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
元表
Lua 中的每个值都可以有一个 元表。 这个 元表 就是一个普通的 Lua 表, 它用于定义原始值在特定操作下的行为。 如果你想改变一个值在特定操作下的行为,你可以在它的元表中设置对应域。 例如,当你对非数字值做加操作时, Lua 会检查该值的元表中的 "__add
" 域下的函数。 如果能找到,Lua 则调用这个函数来完成加这个操作。
- 在元表中事件的键值是一个双下划线(
__
)加事件名的字符串; 键关联的那些值被称为 元方法。 - 使用
setmetatable
来替换一张表的元表。 - 元表决定了一个对象在数学运算、位运算、比较、连接、 取长度、调用、索引时的行为。
- 元表还可以定义一个函数,当表对象或用户数据对象在垃圾回收时调用它。
- 对于一元操作符(取负、求长度、位反), 元方法调用的时候,第二个参数是个哑元,其值等于第一个参数。 这样处理仅仅是为了简化 Lua 的内部实现 (这样处理可以让所有的操作都和二元操作一致), 这个行为有可能在将来的版本中移除。 (使用这个额外参数的行为都是不确定的。)
local t = {v=14}
local t2 = {10}
local addT = {
__add = function(i,j)
return i.v+1
end,
__index = function(table, index)
return "null"
end
-- 类似构造函数
__newindex = function(table,key,value)
-- 类似super
rawset(table, key,value)
end,
}
local addT2 = {
__add = function(i,j)
return i[1]+1
end,
__index = {
a = "a",
b = "b"
}
}
setmetatable(t,addT)
setmetatable(t2,addT2)
print(t+1) --15
print(t2+1) -- 11
print(t["a"]) -- null
print(t2["a"]) -- a
print(t2["c"]) --nil
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
面向对象
语法糖使用
local object = {
value = 4,
add = function(tab, sum)
tab.value = tab.value + sum
end,
}
object:add(3)
print(object.value) -- 7
-- 实现一个list
list = {}
-- 定义提供的调用接口
listmt = {
add = function(t, value)
table.insert(t.items,value)
end,
get = function(t, index)
return t.items[index]
end,
remove = function(t, index)
if not index then
index = #t.items
end
table.remove(t.items,index)
end,
}
listmt["__index"] = listmt
-- 定义构造函数
function list.new()
local t = {
items= {}
}
setmetatable(t, listmt)
return t
end
-- 对象创建及调用
local obj = list.new()
obj:add("a")
obj:add("b")
obj:add("c")
print(obj:get(2)) -- b
obj:remove(1)
print(obj:get(1)) -- b
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 协程
描述
Lua 支持协程,也叫 协同式多线程。 一个协程在 Lua 中代表了一段独立的执行线程。 然而,与多线程系统中的线程的区别在于, 协程仅在显式调用一个让出(yield)函数时才挂起当前的执行
调用函数coroutine.create
可创建一个协程。 其唯一的参数是该协程的主函数。 create 函数只负责新建一个协程并返回其句柄 (一个 thread 类型的对象); 而不会启动该协程。
调用 coroutine.resume
函数执行一个协程。
第一次调用 coroutine.resume
时,第一个参数应传入 coroutine.create
返回的线程对象,然后协程从其主函数的第一行开始执行。
传递给coroutine.resume
的其他参数将作为协程主函数的参数传入。
协程启动之后,将一直运行到它终止或 让出。
协程的运行可能被两种方式终止:
- 正常途径是主函数返回 (显式返回或运行完最后一条指令),
coroutine.resume
将返回 true, 并接上协程主函数的返回值。 - 非正常途径是发生了一个未被捕获的错误, 当错误发生时,
coroutine.resume
将返回 false 与错误消息。
通过调用coroutine.yield
使协程暂停执行,让出执行权。 协程让出时,对应的最近 coroutine.resume
函数会立刻返回,即使该让出操作发生在内嵌函数调用中 (即不在主函数,但在主函数直接或间接调用的函数内部)。
在协程让出的情况下,coroutine.resume
也会返回 true, 并加上传给 coroutine.yield
的参数。 当下次重启同一个协程时, 协程会接着从让出点继续执行。 此时,此前让出点处对 coroutine.yield
的调用 会返回,返回值为传给 coroutine.resume
的第一个参数之外的其他参数。
示例
-- 创建一个协程
co = coroutine.create(function(arg1, arg2)
print('create', arg1,arg2)
-- 暂停协程
local v1, v2 = coroutine.yield(co, 'yield 111', 'yield 222')
print('yield', v1, v2)
return 'create'..arg1, 'create'..arg2
end
);
-- 执行协程 直至结束或暂停
val1, val2 = coroutine.resume(co, '111', '222')
print('resume1', val1, val2)
-- 再次执行、或从暂停处继续执行
val1, val2 = coroutine.resume(co, '333', '444')
print('resume2', val1, val2)
-- 使用 ccoroutine.wrap封装
co = coroutine.wrap(function(arg1, arg2)
print('create', arg1,arg2)
local v1, v2 = coroutine.yield(co, 'yield 111', 'yield 222')
print('yield', v1, v2)
return 'create'..arg1, 'create'..arg2
end
);
val1, val2 = co('111', '222')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29