Lua简介
Lua 是一门强大、高效、轻量级、可嵌入的脚本语言。其设计目标是成为一个能够轻松与其它语言(尤其是 C 和 C++)编写的代码集成的、易于使用的扩展语言。
核心特性
轻量级 |
体积非常小。完整的 Lua 解释器在经过压缩后只有几百 KB。编译速度快,运行开销低。 |
可嵌入 |
这是 Lua 最核心的设计目标。它可以非常方便地嵌入到应用程序(如游戏引擎、网络网关)中,作为该应用的脚本接口,为用户提供灵活的定制和扩展能力。 |
高效 |
拥有非常快的执行效率。其虚拟机(Lua VM)是最快的脚本语言虚拟机之一。 |
跨平台 |
由于 Lua 完全由 ANSI C 编写,它可以在所有主流操作系统(Windows, Linux, macOS, UNIX, Android, iOS 等)以及各种嵌入式系统和微控制器上运行。 |
补充
Lua是一门动态强类型语言。它的灵活性来自于类型的动态性(运行时确定),而它的安全性和严谨性来自于类型的强度(限制隐式转换)。这种结合使得Lua既灵活好用,又不容易产生因隐式转换导致的难以察觉的bug。
典例子:数字和字符串的拼接
local result = "Hello, I am " .. 25 -- 正确:数字 25 被显式地转换成了字符串 "25"
print(result) -- 输出: Hello, I am 25
local result = 10 + "5" -- 错误!attempt to perform arithmetic (add) on number and string
第一行代码能成功,是因为字符串连接操作符 ..
显式要求其两边的操作数都必须是字符串。如果不是,Lua会调用tostring()
函数将它们转换为字符串。这是一种显式的规则,而不是随意的隐式转换。
第二行代码会抛出错误。算术操作符 +
期望两边都是数字,而Lua不会自动将字符串 "5"
转换成数字 5
来满足这个操作。你必须自己转换:10 + tonumber("5")
。
运行环境搭建
Sublime Text快速搭建Lua语言运行环境_sublime lua-CSDN博客
基本规范
1、Lua文件的后缀名是.Lua
2、变量
变量命名:Lua语言中的标识符是由任意字母、数字和下画线组成的字符串
注意:
(1)不能以数字开头
(2)“下画线 +大写字母”组成的标识符通常被lua语言用作特殊用途,应避 免使用。
变量类型:使用local修饰的变量是局部变量,未使用local修饰的变量是全局变量
3、打印
4、注释
5、分隔符;
在Lua中每个语句的分隔符";"不是必须的,可写可不写,根据自己的编码习惯决定即可。
6、if else
local a = 1
local b = 2
----------第一种
if a == b then
end
----------第二种
if a == b then
else
end
----------第三种
if a == b then
elseif a < b then
else
end
数据类型
Lua语言有8种基本类型:
nil |
boolean |
number |
string |
table |
function |
thread |
userdata |
空 |
布尔 |
数值 |
字符串 |
表 |
函数 |
线程 |
用户数据 |
nil 空
1、nil 类型表示无效值,它只有一个值 nil。
2、把nil赋值给一个全局变量时,相当于将该变量删除,Lua会回收该全局变量占用的内存。
boolean 布尔
boolean 类型只有两个可选值:true(真) 和 false(假)。但Lua 把 false 和 nil 都看作是 false,其他的都为 true(数字 0 和空字符串也是 true)。
number 数值
1、number 类型有两种内部表现方式, 整数 和 浮点数。
2、当需要区分整型值和浮点型值时,可以使用函数math.type
3、整数和浮点数的除法
string 字符串
字符串的声明
1、使用双引号
2、使用单引号
3、使用一对双方括号来声明长字符串/多行字符串。方括号括起来的内容可以包括很多行,并且内容中的转义序列不会被转义
获取字符串长度
字符串拼接
local str = "好好学习".."天天向上"
字符串反转
字符串转数值
字符串转数值,如果转换失败,会返回nil
变量转字符串
重复拼接字符串
截取字符串
也支持负数索引,-1代表最后一个字符,-2代表倒数第二个字符,以此类推
替换字符串
共有四个参数,第四个参数是设置替换的次数,如果不传则全部替换
转大写字母
转小写字母
查找子串
若找到,则返回子串的开始位置和结束位置;若未找到,则返回nil。
字符串格式化
--格式化一个整数 %d
local str1 = string.format("我的分数是:%d",25)
--格式化一个浮点数 %f
local str2 = string.format("圆周率:%f",3.25)
--格式化一个字符串 %s
local str3 = string.format("我的名字:%s","卡布奇洛")
--添加占位符
local str4 = string.format("%04d",56)
table 表
1、表是Lua语言中最主要和强大的数据结构。可以使用表来表示数组、列表、字典等数据结构。2、表是不固定大小的,可以自动扩容,体现了它的灵活性。
3、当程序中不再有指向一个表的引用时,垃圾收集器会最终删除这个表并重用其占用的内存。
4、用表来实现列表时,是以1为初始索引。(C#是以0开始索引)
表的创建和初始化
表的索引
可以用任意类型的值作为表的索引,但这个值不能为nil
易混淆:
表的遍历
使用数值型for循环
遍历输出所有元素,即使遇到nil也输出
ipairs迭代器遍历
遇到nil会退出
pairs迭代器遍历
遇到nil会跳过,不会退出,继续打印输出后续元素
注意
当表表现字典时
ipairs只能遍历键为数值的字典,并且碰到
1、键为0
2、键为负数
3、值为nil
4、索引不连续
就会退出
pairs可以遍历键为任意数据类型的字典,可以遍历键为0、负数、索引不连续的情况不会退出,遇到值为nil的情况会跳过,输出的情况可能不是表中对应的顺序
表的操作
获取表的长度 |
table.getn(tab) #tab |
连接表中元素 |
1、table.concat(tab),无缝拼接元素 2、table.concat(tab,s),以字符串s作为分隔符进行拼接 3、table.concat(tab,s,start),从start索引开始,以字符串s作为分隔符进行拼接 4、table.concat(tab,s,start,end)从start索引开始到end索引结束,以字符串作为分隔符进行拼接 |
向表中插入数据 |
1、table.insert(tab,value) 向表tab末尾插入value 2、table.insert(tab,pos,vlaue) 向表tab的pos索引位置插入value |
删除表中数据 |
1、table.remove(tab) 删除tab中最后一个元素 2、table.remove(tab,pos) 删除tab中pos位置的元素 上述都返回被删除的值 |
对表中元素进行排序 |
--默认为升序 table.sort(tab) --降序 table.sort(tab,function(a,b) return a>b end) |
插入元素
local list = {1,2,3,4,5,6}
--重载方法1 在列表末尾插入元素
table.insert(list,666)
--打印:1,2,3,4,5,6,666
--重载方法2 在列表指定索引位置插入元素
--例子:在列表list的第3个索引位置处插入元素666
table.insert(list,3,666)
--打印:1,2,666,3,4,5,6
删除元素
local list = {1,2,3,4,5,6}
--重载方法1 从列表末尾删除元素
table.remove(list)
--打印:1,2,3,4,5
--重载方法2 删除列表指定索引位置的元素
table.remove(list,3)
--打印:1,2,4,5,6
拼接元素
是一个非常高效的方法,用于将数组表中的所有字符串元素连接成一个字符串。它比使用 ..
运算符进行多次连接要高效得多。
类似于C#中的string.Join方法
local list = {1,2,3,4,5,6}
local result
--重载方法1 无缝拼接元素
result = table.concat(list)
--打印:123456
--重载方法2 用分隔符进行拼接
result = table.concat(list,",")
--打印:1,2,3,4,5,6
--重载方法3 从start索引开始,用分隔符进行拼接
result = table.concat(list,",",3)
--打印:3,4,5,6
--重载方法4 从start索引开始到end索引结束,用分隔符进行拼接
result = table.concat(list,",",2,5)
--打印:2,3,4,5
元素排序
-----------------单元素排序----------------------
local list = {3,1,5,2,4}
--升序排序
--打印:1, 2, 3, 4, 5
table.sort(list)
--降序排序
--打印:5, 4, 3, 2, 1
table.sort( list, function(a,b)
return a > b
end)
-----------------多条件排序----------------------
local list = {
{id = 11,score = 606,num = 1},
{id = 33,score = 505,num = 3},
{id = 33,score = 505,num = 2},
{id = 44,score = 303,num = 5},
{id = 11,score = 505,num = 4},
}
table.sort( list, function(a,b)
if a.id ~= b.id then
return a.id < b.id
end
if a.score ~= b.score then
return a.score > b.score
end
return a.num > b.num
end)
for k,v in ipairs(list) do
print(v.id,v.score,v.num)
end
参数打包
table.pack用于将可变数量的参数打包成一个表,并额外包含一个 n
字段来表示参数的数量。
-- 示例 1: 基本用法
local t1 = table.pack(1, 2, 3, "hello")
print(t1.n) -- 输出: 4
print(t1[1]) -- 输出: 1
print(t1[4]) -- 输出: hello
-- 示例 2: 处理 nil 值
local t2 = table.pack(1, nil, 3, nil)
print(t2.n) -- 输出: 4
print(t2[2]) -- 输出: nil (不会被截断)
-- 示例 3: 空参数
local t3 = table.pack()
print(t3.n) -- 输出: 0
实际应用:处理可变参数函数
function processArgs(...)
local args = table.pack(...)
print("参数个数:", args.n)
for i = 1, args.n do
print("参数 " .. i .. ":", args[i])
end
end
processArgs("a", "b", "c")
-- 输出:
-- 参数个数: 3
-- 参数 1: a
-- 参数 2: b
-- 参数 3: c
参数解包
table.unpack用于将表中的元素展开为多个返回值。
-- 示例 1: 基本展开
local t = {10, 20, 30, 40}
local a, b, c = table.unpack(t)
print(a, b, c) -- 输出: 10 20 30
-- 示例 2: 指定范围
local x, y, z = table.unpack(t, 2, 4)
print(x, y, z) -- 输出: 20 30 40
-- 示例 3: 函数调用
local params = {"hello", "world", 123}
print(table.unpack(params)) -- 输出: hello world 123
与直接使用 ...
的区别
function test1(...)
-- 直接使用 ...,无法知道参数个数(如果有 nil 会被截断)
local args = {...}
print("参数个数(可能不准确):", #args)
end
function test2(...)
-- 使用 table.pack,准确获取参数个数
local args = table.pack(...)
print("准确参数个数:", args.n)
end
test1(1, nil, 3) -- 输出: 参数个数(可能不准确): 1
test2(1, nil, 3) -- 输出: 准确参数个数: 3
function 函数
--形式1
function Test1()
print("Hello World!1")
end
--形式2
local Test2 = function()
print("Hello World!2")
end
多返回值
可变长参数
--计算平均值
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
return result/#arg
end
print("平均值为",average(10,5,3,4,5,6))
3、点和冒号的区别:
点用于表直接调用表内部成员
冒号是将调用者作为第一个参数传入到函数中。使用self关键字就可以对调用者进行交互。
local Animal =
{
name = "动物",
height = 10,
weight = 20,
}
--self就是指代Animal表对象
function Animal:Test()
print("动物的名字:",self.name)
end
Animal.Say = function ()
print("动物叫声!")
end
Animal:Test()
Animal.Say()
thread 线程
方法 |
描述 |
coroutine.create(func) |
创建 coroutine,返回 coroutine一个线程, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() |
重启 coroutine,和 create 配合使用 |
coroutine.wrap(func) |
创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.status() |
查看 coroutine 的状态 |
coroutine.yield() |
挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.running() |
返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号 |
--使用coroutine.create()创建协程
co=coroutine.create(function ()
print("协程")
end)
--协程的状态
print(coroutine.status(co))
--
coroutine.resume(co)
--使用coroutine.wrap()创建协程
co1=coroutine.wrap(function()
print("第二个协程")
end)
co1()
--协程挂起
function Add()
local a=0
while(true)do
a=a+1
print(a)
coroutine.yield(a)
end
end
--用create方法时,接收yield的第一个返回值是boolean
co2=coroutine.create(Add)
local x,y=coroutine.resume(co2)
print(x,y)
x,y=coroutine.resume(co2)
print(x,y)
co3=coroutine.wrap(Add)
local m,n=co3()
print(m,n)
m,n=co3()
print(m,n)
循环控制
for循环
顺序遍历
local list = {1,2,3,4,5}
--打印:1,2,3,4,5
for i = 1,#list do
print(list[i])
end
倒序遍历
local list = {1,2,3,4,5}
--打印:5,4,3,2,1
for i = #list,1,-1 do
print(list[i])
end
间隔遍历
例如,间隔2个单位
local list = {1,2,3,4,5}
--打印:1,3,5
for i = 1,#list,2 do
print(list[i])
end
while循环
先判断再运行
local x = 5
while(x > 0)do
x = x -1
end
repeat untile循环
先运行一次再判断
local x = 5
repeat
x = x - 1
until(x <= 0)
运算符
算术运算符
操作符 |
描述 |
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
% |
取余 |
^ |
乘幂 |
- |
负号 |
关系运算符
操作符 |
描述 |
== |
等于 |
~= |
不等于 |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
逻辑运算符
操作符 | 描述 | 实例 |
---|---|---|
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。 |
and、or、 not分别类似于C#中的 &&、||、!
and 和or遵循短路求值原则,即只在必要时才对第二个操作数进行求值。
and:如果第一个值为真则返回第二个值,如果第一个值为假则直接返回第一个值
or:如果第一个值为真则返回第一个值,如果第一个值为假则返回第二个值
实现三目运算符
local a = 1
local b = 2
--打印较大值:2
local result = a > b and a or b
运算符优先级
从高到低的顺序
错误处理
error函数
功能:终止正在执行的函数,并返回message的内容作为错误信息
assert函数
assert首先检查第一个参数,若为true,assert不做任何事情;若为false,assert以第二个参数作为错误信息抛出。
数学函数
abs |
取绝对值 |
math.abs(-15) |
15 |
acos |
反余弦函数 |
math.acos(0.5) |
1.04719755 |
asin |
反正弦函数 |
math.asin(0.5) |
0.52359877 |
atan2 |
x / y的反正切值 |
math.atan2(90.0, 45.0) |
1.10714871 |
atan |
反正切函数 |
math.atan(0.5) |
0.463647609 |
ceil |
不小于x的最大整数 |
math.ceil(5.8) |
6 |
cosh |
双曲线余弦函数 |
math.cosh(0.5) |
1.276259652 |
cos |
余弦函数 |
math.cos(0.5) |
0.87758256 |
deg |
弧度转角度 |
math.deg(math.pi) |
180 |
exp |
计算以e为底x次方值 |
math.exp(2) |
2.718281828 |
floor |
不大于x的最大整数 |
math.floor(5.6) |
5 |
fmod (mod) |
取模运算 |
math.mod(14, 5) |
4 |
frexp |
把双精度数val分解为数字部分(尾数)和以2为底的指数n,即val=x*2n |
math.frexp(10.0) |
0.625 4 |
ldexp |
计算value * 2的n次方 |
math.ldexp(10.0, 3) |
80 = 10 * (2 ^3) |
log10 |
计算以10为基数的对数 |
math.log10(100) |
2 |
log |
计算一个数字的自然对数 |
math.log(2.71) |
0.9969 |
max |
取得参数中最大值 |
math.max(2.71, 100, -98, 23) |
100 |
min |
取得参数中最小值 |
math.min(2.71, 100, -98, 23) |
-98 |
modf |
把数分为整数和小数 |
math.modf(15.98) |
15 98 |
pow |
得到x的y次方 |
math.pow(2, 5) |
32 |
rad |
角度转弧度 |
math.rad(180) |
3.14159265358 |
random |
获取随机数 |
math.random(1, 100) |
获取1-100的随机数 |
randomseed |
设置随机数种子 |
math.randomseed(os.time()) |
在使用math.random函数之前必须使用此函数设置随机数种子 |
sinh |
双曲线正弦函数 |
math.sinh(0.5) |
0.5210953 |
sin |
正弦函数 |
math.sin(math.rad(30)) |
0.5 |
sqrt |
开平方函数 |
math.sqrt(16) |
4 |
tanh |
双曲线正切函数 |
math.tanh(0.5) |
0.46211715 |
tan |
正切函数 |
math.tan(0.5) |
0.5463024 |
举例:除数取整
随机数
math.random() |
返回一个在[0,1)范围内的伪随机小数 |
math.random(x) |
返回一个在[1,x]范围内的伪随机整数 |
math.random(x,y) |
返回一个在[x,y]范围内的伪随机整数 |
math.randomseed(x) |
1、math.randomseed(x)用于设置伪随机数发生器的种子,系统默认x为1 2、若x相同,则生成的伪随机数序列也相同,要想生成的随机数序列不同则将x设置伪不同的值 3、使用math.randomseed(os.time())来使用当前系统时间来初始化 |
--对随机性要求不高的情况
math.randomseed(os.time())
print(math.random(1,20))
--对随机性要求较高的情况
local seed = tonumber(tostring(os.time()):reverse():sub(1,6))
math.randomseed(seed*1000)
local num = math.random(1, 20)
--生成随机小数
function GetRandom(minValue,maxValue)
local value = math.random()
return minValue+value*(maxValue-minValue)
end
--生成随机小数(限制位数)
function GetRandom(minValue,maxValue,decimalPlace)
local value = math.random()
local result = minValue+value*(maxValue-minValue)
local format = "%."..decimalPlace.."f"
result = string.format(format,result)
result = tonumber(result)
return result
end
日期和时间
os.time
获取当前时间戳
os.date
格式化为日期表
字段名 |
全称 |
含义 |
取值范围 |
示例 |
|
Year |
年份(四位数字) |
通常 >= 1970 |
|
|
Month |
月份 |
1 (一月) - 12 (十二月) |
(代表六月) |
|
Day |
当月的第几天 |
1 - 31 |
|
|
Year Day |
当年的第几天 |
1 - 366 |
(在非闰年中代表6月15日) |
|
Week Day |
本周的第几天 |
1 (星期日) - 7 (星期六) |
(代表星期六) |
|
Hour |
当前小时 (24小时制) |
0 - 23 |
(下午2点) |
|
Minute |
当前分钟 |
0 - 59 |
|
|
Second |
当前秒数 |
0 - 61 (允许闰秒) |
|
|
Is Daylight Saving Time |
是否处于夏令时 |
( / ) |
(中国不实行夏令时) |
wday
的取值范围: 请注意,wday
是从 星期日(Sunday)开始 的。
1
= 星期日、2
= 星期一、3
= 星期二、4
= 星期三、5
= 星期四、6
= 星期五、7
= 星期六
生成当前时间的日期表
生成指定时间戳的日期表
格式化时间字符串
示例1:
local list =
{
"%a","%A","%b","%B","%c",
"%H","%I","%j","%m","%M",
"%p","%S","%w","%W","%x",
"%X","%y","%Y"
}
for k,v in ipairs(list)do
print("指示符:",v,"打印:",os.date(v))
end
示例2:
local date = os.date("%Y/%m/%d %H:%M:%S")
模块和包
模块类似于一个封装库,可以把一些公用的代码放在一个模块文件中,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
_G表
_G是一个总表,他将我们声明的所有全局变量都存储在其中,局部变量不会存到该表中(加了local的变量)
for k,v in pairs(_G) do
print(k,v)
end
加载文件步骤
1、注册自定义文件加载路径
lua一般会从默认的路径 package.path 进行加载文件,但是有时候我们需要从自己指定的文件夹路径中加载。所以,我们需要在加载文件之前进行注册自定义文件加载路径
local custom_path =
{
"C:\\Users\\24292\\Desktop\\LuaProject\\?.lua",
"C:\\Users\\24292\\Desktop\\LuaAnother\\?.lua",
}
for k,v in pairs(custom_path) do
package.path = package.path..";"..v
end
2、创建公共代码文件
C:\Users\24292\Desktop\LuaAnother\MyMath.lua
local MyMath = {}
MyMath.Add = function(a,b)
return a+b
end
return MyMath
3、创建测试文件
C:\Users\24292\Desktop\LuaProject\Main.lua 使用require加载文件
local custom_path =
{
"C:\\Users\\24292\\Desktop\\LuaProject\\?.lua",
"C:\\Users\\24292\\Desktop\\LuaAnother\\?.lua",
}
for k,v in pairs(custom_path) do
package.path = package.path..";"..v
end
local MyMath = require("MyMath")
local result = MyMath.Add(1,2)
print(result)
require、dofile、loadfile的功能和区别
一般require比较常用,其他两个函数作了解
require
1、加载并执行指定的模块
2、对于搜索路径,不需要写文件的全路径和.lua扩展名。require
会根据 package.path
(用于 Lua 文件)和 package.cpath
(用于 C 库)中定义的模板路径来搜索文件。
3、加载后会将该模块缓存起来,多次加载时只会执行一次。这确保了模块的 Singleton(单例)特性,避免了重复定义和资源浪费。
dofile
1、编译并立即执行指定的 Lua 文件。
2、搜索路径需要写文件的全路径和.lua扩展名
2、加载后没有缓存,每次调用,文件都会被重新编译和执行。
loadfile
1、将指定的 Lua 文件编译成 chunk(一个代码块),并将其作为一个函数返回。它并不执行这个函数。执行该函数后,才能获取加载后返回的模块对象。
2、搜索路径需要写文件的全路径和.lua扩展名。
3、加载后没有缓存,每次调用,文件都会被重新编译和执行。
元表和元方法
元表(Metatable)和元方法(Metamethod)是Lua中实现面向对象编程、操作符重载和自定义行为的重要机制。
元表
元表是一个普通的Lua表,可以附加到另一个表上,用于定义或修改该表的行为。每个表都可以有自己的元表。
setmetatable(tab,metatab) |
将metatab设置为tab的元表 |
getmetatable(tab) |
获取表tab的元表 |
local t = {}
local mt = {} -- 元表
-- 设置元表
setmetatable(t, mt)
-- 获取元表
local mt_of_t = getmetatable(t)
元方法
元方法是定义在元表中的特殊键,当表参与特定操作时会被调用。
运算相关元方法
local Calculate = {}
local data1 = {number = 2}
local data2 = {number = 4}
setmetatable(data1,Calculate)
setmetatable(data2,Calculate)
--加
Calculate.__add = function(a,b)
return a.number+b.number
end
print(data1 + data2)
--减
Calculate.__sub = function(a,b)
return a.number-b.number
end
print(data1 - data2)
--乘
Calculate.__mul = function(a,b)
return a.number*b.number
end
print(data1 * data2)
--除
Calculate.__div = function(a,b)
return a.number/b.number
end
print(data1 / data2)
--取余
Calculate.__mod = function(a,b)
return a.number%b.number
end
print(data1 % data2)
--等于判断
Calculate.__eq = function(a,b)
return a.number == b.number
end
print(data1 == data2)
--连接符
Calculate.__concat = function(a,b)
return a.number .. b.number
end
print(data1..data2)
--小于号
Calculate.__lt = function(a,b)
return a.number < b.number
end
print(data1<data2)
--小于或等于
Calculate.__le = function(a,b)
return a.number <= b.number
end
print(data1 <= data2)
--幂运算
Calculate.__pow = function(a,b)
return a.number ^ b.number
end
print(data1 ^ data2)
--负数
Calculate.__unm = function(a)
return -a.number
end
print(-data1)
测试打印:
库定义相关元方法
__tostring
当要打印表名时,Lua就会查找该表的元表中的__tostring方法,并调用。
local animal = {
name = "动物"
}
local cat = {
name = "小猫咪"
}
setmetatable(cat,animal)
animal.__tostring = function(t)
print(t.name)
return "动物方法"
end
print(cat)
__call
当表被当做一个函数被调用时,Lua就会查找该表的元表中的__call方法,并调用
local animal = {
name = "动物"
}
local cat = {
name = "小猫咪"
}
setmetatable(cat,animal)
animal.__call = function(t)
print("我是"..t.name.."的方法")
end
cat()
表相关元方法
__index
当访问一个表中不存在的字段时,那么Lua就会寻找该table的metatable中的__index 键
元方法是一个表
local Human = {}
Human.__index = {
score = 250
}
local Student = {}
setmetatable(Student,Human)
print(Student.score)
元方法是一个函数
local Human = {}
Human.__index = function()
print("调用index元方法")
return 1000
end
local Student = {}
setmetatable(Student,Human)
print(Student.score)
__newindex
当给表中一个不存在的键赋值时,首先判断该表是否有元表,如果没有则相当于直接在表中声明一个变量并赋值,如果有则在元表中查找__newindex键,如果__newindex包含一个表则直接在该表中赋值,不在原始表中赋值。
__newindex 元方法用来对表更新,__index则用来对表访问 。
rawset
在不触发元方法__newindex的情况下,在原始表中进行声明赋值.
未用rawset情况:如果直接赋值的话,会查找tab2的元表中的元方法__newindex,将在元表tab1中声明该键并赋值,而不在tab2中声明赋值
使用rawset情况:当元表中有元方法__newindex时,使用rawset,给tab2中的不存在的键赋值,不会调用元方法__newindex,直接在tab2中声明赋值
rawget
在不触发元方法__index的情况下,直接在原始表中查找该字段
未用rawget情况:会查找元表中的元方法__index
local Human = {
score = 100
}
Human.__index = {
score = 250
}
local Student = {}
setmetatable(Student,Human)
print(rawget(Student,"score"))
使用rawset情况
local Human = {
score = 100
}
Human.__index = {
score = 250
}
local Student = {}
setmetatable(Student,Human)
print(rawget(Student,"score"))
垃圾收集
Lua 采用了自动内存管理。不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一个 垃圾收集器 来收集所有 死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
collectgarbage("collect") |
做一次完整的垃圾收集循环。 这是默认选项。 |
collectgarbage("count") |
以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。 |
collectgarbage("restart") |
重启垃圾收集器的自动运行。 |
collectgarbage("stop") |
停止垃圾收集器的运行 |