【Lua】知识杂记
2 变量
2.1 概念
Lua是一种动态类型语言
Lua中的所有变量申明都不需要申明变量类型,它会自动地识别类型。并且对于lua中的一个变量,可以随便赋值,不会出现C#里会编译错误的情况(因为C#是强类型语言,一个变量一旦被声明类型,就不能更改其类型)。
Lua
1 | a = 1 |
C#
1 | int a = 1; |
在Lua中可以直接使用没有初始化的变量,默认为nil。
1 | print(b) |
type()
在Lua中我们可以通过type函数获得变量的类型,并且type()返回的其实是一个string类型的数值。
1 | a = nil |
2.2 简单的四种变量类型
2.2.1 四种变量类型
- nil:空
- number:数值类型,如:1,1.2…
- string:字符串
- boolean:true/false
2.2.2 string
概念
Lua中字符串的声明,使用单引号或双引号包裹,二者没有区别。
1 | a = '12345' |
方法
获取字符串的长度
1 | -- 使用#获取字符串长度 |
字符串多行打印
1.支持转义字符
1 | str = "123\n123" |
2.使用[[]]
1 | str = [[ |
字符串拼接
使用 ..
1 | str1 = "123" |
使用string.format
1 | print(string.format("月亮与%d便士",6)) |
别的类型转字符串
1 | a = true |
其他公共方法
1 | str = "abcDEF" |
2.3 复杂的四种变量类型
- function:函数
- table:表
- userdata:数据结构
- thread:协同程序(线程)
3 运算符
3.1 算数运算符(+ - …)
1 | -- + - * / % ^ |
1 | print("123"+1) -- 结果:124,区别于C#,C#里会等于1231 |
1 | print("2"^2) -- 结果:4,幂运算,区别于C#,C#里是异或运算符(相同为false,不相同为true) |
3.2 条件运算符(> < …)
1 | -- > < ≥ ≤ == ~=(不等于) |
3.3 逻辑运算符(and or not)
3.3.1 概念
在lua中,只有nil和false为假。
3.3.2 逻辑运算符的短路现象
1 | --在C#中,如果&&前面的条件已经为false,那么不会继续判断后面的条件,这就叫做短路现象 |
3.3.3 替代三目运算符*
C#里的三目运算符:? : ,我们可以用他们来进行这样的判断:
1 | var a = 1; |
而在lua里没有三目运算符,我们通过这样的方式实现上面的判断:
1 | a = 1 |
这里的逻辑:
b>=a已经为true,对于and来说,结果取决于 b or a。
而对于or来说,b非false,结果为b。
那么假设b>=a此时为false呢?
那么对于and来说,b>=a and b的结果为false,那么式子演变为:false or a
对于or来说,前面为false,结果取决于a,即结果为a。
4 语句
4.1 条件分支语句
1 | -- if语法:if 条件 then....end |
4.2 循环语句
4.2.1 while
1 | -- while 条件 do...end |
4.2.2 repeat…until
1 | -- repeat...until 结束条件 |
4.2.3 for
1 | -- for 默认i递增 |
如果想修改i的变化
1 | -- for i+2 |
5 函数(function)
5.1 函数
无参无返回值
1 | ------------无参无返回值 |
有参无返回值
1 | ------------有参数 |
有返回值
1 | ------------有返回值 |
5.2 变长参数
1 | ------------变长参数 |
5.3 函数嵌套
1 | ------------函数闭包 |
5.4 闭包
通过捕获外部变量的方式改变其生命周期。
1 | ------------函数闭包 |
6 表(table)
表是一切复杂数据结构,如数组、字典、类等等的基础,通过表我们可以表示出各种数据结构的特征,但它们本质上都是表。
6.1 “数组”
6.1.1 一维数组
1 | a = {1,2,3,"Hello",true} |
6.1.2 二维数组
1 | a = {{1,2,3},{4,5,6}} |
6.1.3 自定义索引
1 | a = {[0] =1,2,3,4} |
6.1.4 在数组中加入nil
如果数组中某一位为nil,会影响#获取的长度,如下实验所示:
所以一般不在数组中添加nil。
6.2 迭代器遍历
迭代器遍历一般是用来遍历表的,因为对于不规则表(如添加了nil或自定义索引的)使用#获得的长度并不准确,导致遍历时并非所有元素都能访问到。
6.2.1 ipairs
和#相似,对于不规则表的访问并不准确。
1.访问不到自定义索引值小于等于0的元素。
2.如果从1开始,索引顺序断了,后面的内容也找不到。
例如在这里我们把上面例子里最后的自定义索引改为3。
6.2.2 pairs
pairs的访问则较为准确。
6.3 “字典”
1 | ------------字典 |
6.4 “类”
Lua默认是没有面向对象的。
6.4.1 概念
1 | ------------类 |
6.4.2 .和:调用方法的区别
1 | ------------调用 Lua中.和:的区别,冒号调用方法时会默认把调用者作为第一个参数传入 |
6.4.3 self关键字
1 | ------------self |
6.5 table的公共操作
6.5.1 insert
1 | t1 = {1,2,3} |
6.5.2 remove
1 | t1 = {1,2,3} |
6.5.3 sort
1 | t1 = {3,2,1} |
6.5.4 concat
1 | t1 = {"123","456","789"} |
7 多Lua脚本执行
7.1 全局变量和本地变量
全局变量
1 | --这样声明都是全局变量 |
本地变量(local)
1 | --local是本地变量/局部变量的关键字 |
7.2 多脚本执行
关键字:require(“脚本名”)。
lalala.lua
1 | print("This is lalala") |
helloworld.lua
1 | print("Hello,World!") |
结果
——————————奇怪的用法
使用require加载脚本时,脚本的本地变量可以return出去给别的脚本用,如:
lalala.lua
1 | local a = 1 |
helloworld.lua
1 | print(require("lalala")) |
7.3 脚本卸载
要解决的问题是:require加载执行的脚本,加载一次后不会再被执行。
1 | print("Hello,World!") |
package.loaded[“脚本名”]
可以通过这个方法得到指定脚本是否有被加载过。
卸载:
1 | package.loaded["脚本名"] = nil |
7.4 大G表
大G表是一个总表,它将我们声明的全局变量都存储在其中。
关键字:_G。
1 | for i,k in pairs(_G) do |
8 协程
8.1 协程的创建
coroutine.create(方法)
1 | fun = function() |
coroutine.wrap(方法)
1 | fun = function() |
8.2 协程的运行
coroutine.resume(协程)
1 | --对应用create创建的协程 |
函数调用
因为用wrap创建的协程本质上是函数,因此可以用函数调用的方式。
1 | --对应用wrap创建的协程 |
8.3 协程的挂起
coroutine.yield()
1 | fun = function () |
另外,yield()里面是可以返回值的。
另外,使用wrap()创建的协程没有默认第一个返回值:
8.4 协程的状态
coroutine.status()
- dead 结束
- suspended 暂停
- running 执行中
coroutine.running()
可以得到当前正在运行的协程的编号。
9 元表
9.1 设置元表
1 | --任何一个表变量都可以作为另一个表变量的元表 |
9.2 特定操作
9.2.1 __tostring
当子表要被当作字符串使用时,会默认调用这个元表中的__tostring方法。
1 | meta = { |
当方法有参数,会默认地把自己传进去。
1 | meta = { |
9.2.2 __call
当子表被当作一个函数来使用时,会默认调用__call方法。
1 | meta = { |
9.2.3 运算符重载
__add
当子表使用+运算符时,会调用该方法。
1 | meta = { |
__sub
当子表使用-运算符时,会调用该方法。
1 | meta = { |
其他
- *:__mul
- /:__div
- %:__mod
- ^:__pow
- ==:__eq(没有~=,直接取反,以下的>和>=同理)
- <:__lt
- <=:__le
- ..:__concat
9.2.4 __index和__newIndex
__index
当子表中找不到某一个属性时,会到元表中__index指定的表去找索引。
1 | meta = { |
或者这么写:
1 | meta = { |
但注意不能这么写:
1 | meta = { |
__newIndex
当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到__newIndex所指的表中,不会修改自己。
1 | meta = { |
rawget
该方法会忽略__index的设置,只会尝试在指定的表中找指定属性。
rawset
该方法会忽略__newIndex的设置,它只会修改指定表的变量。
10 面向对象
- 封装
- 继承
- 多态
10.1 封装
“类”是封装的体现。
1 | Object = {} |
10.2 继承
1 | Object = {} |
10.3 多态
多态即相同行为不同表现:在这里指的是相同方法不同执行逻辑。
1 | --忽略前面的new和subclass的方法 |
以上的逻辑我们确实实现了重写,但与C#不同的是,此时我们想要再调用父类的hello()方法已经不可以了。所以我们应该去实现一个类似于base的概念,让我们在重写后依旧可以调用父类的方法。
1 | function Object:subClass(className) |
11 Lua垃圾回收
获取当前占用字节数
1 | --获取当前占用KB,*1024则是占用字节数 |
垃圾回收
1 | collectgarbage("collect") |