高级概念
- 这部分中会记录一些lua语言中的高级概念和技术。
模块
概念
- 从我的理解看,模块更像是一个类。但是因为lua中没有类的概念,模块的实现是依赖于table。
- 一些公共或者私有的变量+函数组成了模块的主体,最后一个return module完成了基本的构造。其实这个return就是返回了这个table。
- 具体实现看教程
使用
1 | -- 创建一个叫Class的模块 |
1 | -- temp = "local temp1" |
分析一下模块的变量,从例子中可以总结,模块中的以模块名+
.
形成的变量(Class.temp3)是全局变量,而且即使在模块内部调用用也要用模块名+.
的形式使用。没有加模块名的变量其实也是全局变量,外部代码也可以直接访问。注意代码中在require的前后分别定义了一个和模块中名字相同的变量,这里有一个很有意思的事情,就是如果是之前定义的,那么在print时是模块中的值,如果在之后定义那么就是本地定义的值。这说明require这种过程实际上等于在本地定义了一些变量。所以最终输出的值可以按照后定义的输出。
显示标记为local的变量是本地变量,在模块内部使用是没有问题的,但是外部代码无法访问。
加载机制
- 基本上遵循C的加载机制,也就是先找同一目录下的文件,然后会找全局变量中path里面定义的文件。
- 通常我们在编写代码的时候肯定是会有物理的文件夹结构的,此时如果我们不去改package.path的值,那么可以在引用时加速文件夹名字,比如我把class和student放到了Module文件夹下,那么代码中写
require("Module/Class")
即可正常调用。
协程
- 首先是真的多线程,并非unity的那种主线程内部的线程。
1 | require("MyCoroutine") |
- 用法比较繁琐,但是可以明确的控制协程的执行步骤。有点类似python。
- 建议直接看教程。
元表(MetaTable)
metatable更像是lua为table提供的依赖倒置的接口,以
__index
这个函数为例,本身如果lua提供了固定的函数,那么我们在取值时候只能遵从lua的规定。但是现在提供了一个__index
,你可以自定义这个__index
的行为,而lua在运行期间遇到了需要用到__index
的时候就会按照你所规定的准则行事了。这个思维是很好的。下面是
__index
的用法,metatable。
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
3.判断元表有没有index方法,如果index方法为nil,则返回nil;如果index方法是一个表,则重复1、2、3;如果index方法是一个函数,则返回该函数的返回值。
代码示例:
1 | mt = {} |
这个东西有个好处,如果我写的key不存在,那么我是可以自定义它的行为的。我可以返回一个数值,也可以执行一段代码。但是一般来说还是提前预置好数据比较好。
PS:元表本身是一个table,如果按照table就是类这个概念来看的话,元表对应了类类型,而实际的使用中它也是起了这样的作用。元表中以
__
开头的方法们算是它保留的方法,我们可以给一个元表定义这些方法实际是一个什么样的行为。比如__tostring
方法,我们可以在一个元表中重新定义它的实现,有点像C#中override ToString方法。
面向对象
- lua的oop是基于table的,table中可以设置全局或局部的变量、方法等。
- 继承通过子类调用父类的构造函数(其实lua没有构造函数一说,只是我们人为的在代码中创造出来了一个创建对象的函数)来创建,然后在扩展,从而实现了继承。
- 从本质上说,lua的继承和C的继承是一样的,依赖的是table嵌套table,而C中是结构体嵌套结构体。
- 具体看下链接里教程的例子吧。
一些体会
- 其实只要学过一个脚本语言,lua就不算陌生,还算是上手比较快。
- 所有语言的核心在api的使用,所以熟悉标准库是关键。
- lua与C/C++的交互也是大头,不过目前暂时不用就先不学了,等后续学了再填坑。
- 多写,多写,多写。
- 一个学习lua编程的方法是看WOW的插件源码。
调试
- 因为用的是LuaStudio,在调试的过程中也遇到了很多的问题。首先正常的自己写的代码都可以require成功,os\io\math这样的库也都没有问题,但是用到socket库的时候怎么都无法require。最后发现在LuaStudio中必须这样设置才行