Lua学习笔记:Lua高级编程
Lua基础语法上一篇已经介绍过了,这一篇介绍高级概念
模块
模块更像是一个类,但是因为lua中没有类的概念,模块的实现是依赖于table。公共或者私有的变量+函数组成了模块的主体,最后一个return module完成了基本的构造,其实这个return就是返回了这个table。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
local DemoClass = {}
local gameName = "Game1"
gameParentName = "Game"
DemoClass.gameChildName = "GameChild"
function ()
return gameName;
end
return DemoClass
-- 对DemoClass的调用
gameParentName = "haha"
require("DemoClass") -- 导入模块
gameParentName = "haha1"
print(DemoClass.GetGameName) -- Game1
print(DemoClass.gameName) -- nil
print(gameParentName) -- Game
print(DemoClass.gameChildName) -- GameChild
—|—
- 解析一下代码,从例子中可以总结,模块中的以模块名+.形成的变量(DemoClass.gameChildName)是全局变量,而且即使在模块内部调用用也要用模块名+.的形式使用
- 没有加模块名的变量其实也是全局变量,外部代码也可以直接访问。注意代码中在require的前后分别定义了一个和模块中名字相同的变量,这里有一个很有意思的事情,就是如果是之前定义的,那么在print时是模块中的值,如果在之后定义那么就是本地定义的值。这说明require这种过程实际上等于在本地定义了一些变量。所以最终输出的值可以按照后定义的输出。
- 显示标记为local的变量是本地变量,在模块内部使用是没有问题的,但是外部代码无法访问。
加载机制
- 基本上遵循C的加载机制,也就是先找同一目录下的文件,然后会找全局变量中path里面定义的文件。
- 通常我们在编写代码的时候肯定是会有物理的文件夹结构的,此时如果我们不去改package.path的值,那么可以在引用时加速文件夹名字,比如我把class和student放到了Module文件夹下,那么代码中写require(“Module/DemoClass”)即可正常调用。
协程
Lua中的线程是真的多线程,并非unity的那种主线程内部的线程。
Lua
协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
以下用网上的栗子来说明一下
- coroutine.create() 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
- coroutine.resume() 重启 coroutine,和 create 配合使用
- coroutine.yield() 挂起 coroutine
- 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 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 52 53
co = coroutine.create(
function(i)
print(i);
end
)
coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- dead
print("----------")
co = coroutine.wrap(
function(i)
print(i);
end
)
co(1)
print("----------")
co2 = coroutine.create(
function()
for i=1,10 do
print(i)
if i == 3 then
print(coroutine.status(co2)) --running
print(coroutine.running()) --thread:XXXXXX
end
coroutine.yield()
end
end
)
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2)) -- suspended
print(coroutine.running())
print("----------")
-- 结果
1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868 false
suspended
thread: 0x7fb801c04c88 true
----------
—|—
coroutine.running就可以看出来,coroutine在底层实现就是一个线程。当create一个coroutine的时候就是在新线程中注册了一个事件。当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。接下来我们分析一个更详细的实例:
1
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
function foo (a)
print("foo 函数输出", a)
return coroutine.yield(2 * a) -- 返回 2*a 的值
end
co = coroutine.create(function (a , b)
print("第一次协同程序执行输出", a, b) -- co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")
-- 运行结果
第一次协同程序执行输出 1 10
foo 函数输出 2
main true 4
--分割线----
第二次协同程序执行输出 r
main true 11 -9
---分割线---
第三次协同程序执行输出 x y
main true 10 结束协同程序
---分割线---
main false cannot resume dead coroutine
---分割线---
—|—
示例解析:
- 调用resume,将协同程序唤醒,resume操作成功返回true,否则返回false;
- 协同程序运行;
- 运行到yield语句;
- yield挂起协同程序,第一次resume返回;(注意:此处yield返回,参数是resume的参数)
- 第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)
- yield返回;
- 协同程序继续运行;
- 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。
元表
面向对象
- lua的oop是基于table的,table中可以设置全局或局部的变量、方法等
- 继承通过子类调用父类的构造函数(其实lua没有构造函数一说,只是我们人为的在代码中创造出来了一个创建对象的函数)来创建,然后在扩展,从而实现了继承。
- 从本质上说,lua的继承和C的继承是一样的,依赖的是table嵌套table,而C中是结构体嵌套结构体。