Redis学习(14)之Lua语法学习 秒速五厘米 2024-03-25 14:14 12阅读 0赞 #### 文章目录 #### * 前言 * 一 Lua基础语法 * * 1.1 注释 * 1.2 数据类型 * 1.3 标识符 * * 1.3.1 保留字 * 1.3.2 变量 * 1.3.3 动态类型 * 1.3.3 运算符 * * 1.3.4 算术运算符 * 1.3.5 关系运算符 * 1.3.6 逻辑运算符 * 1.3.7 其他运算符 * 1.4 函数 * * 1.4.1 固定参函数 * 1.4.2 可变参函数 * 1.4.3 返回多个值 * 1.4.4 函数作为参数 * 1.5 流程控制语句 * * 1.5.1 if语句&if 嵌套语句 * 1.6 循环控制语句 * 二 Lua 语法进阶 * * 2.1 table * 2.2 table 操作函数 * 2.3 迭代器 * 2.4 模块 * 2.5 元表与元方法 * * 2.5.1 重要函数和方法 * 2.6 面向对象 * 2.7 协同线程 * 2.8 协同函数 * 2.9 文件IO * * 2.9.1 常用静态函数 * 2.9.2 常用实例函数 ## 前言 ## * 学自动力节点,redis课程。本文仅供学习交流使用,禁止用于商业用途!! ## 一 Lua基础语法 ## ### 1.1 注释 ### * 行注释:`--`开头 * 段注释:`--[[`开头,`]]--`结尾 > * 在调试过程中如果想临时取消段注释,而直接将其标识删除,这样做其实并不好。因为有可能还需要再添加上。而段注释的写法相对较麻烦。所以,Lua 给出了一种简单处理方式:在开头的–\[\[前再加一个减号,即可使段注释不起作用。其实就是使两个段注释标识变为了两个行注释。 ### 1.2 数据类型 ### * Lua 中有 8 种类型,分别为:**nil、boolean、number、string、userdata、function、thread 和 table**。 * 通过 `type()`函数可以查看一个数据的类型 * nil:只有值 nil 属于该类,表示一个无效值,与 Java 中的 null 类似。但在条件表达式中相当于 false。 * boolean :包含两个值:false 和 true。 * number: 表示双精度类型的实浮点数。 * string:字符串,由一对双引号或单引号括起来。当一个字符串包含多行时,可以在第一行中以\[\[开头,在最后一行中以\]\]结尾,那么在\[\[与\]\]括起来的这多行内容就是一个字符串。换行符为字符串”\\n”。 * table :类似于 Java 中的数组,但比数组的功能更强大,更灵活。 * function :由 C 或 Lua 编写的函数。 * thread:协同线程,是协同函数的执行体,即正在执行的协同函数。 * userdata:一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据存储到 Lua 变量中调用 ### 1.3 标识符 ### #### 1.3.1 保留字 #### ![在这里插入图片描述][2d84253726fb4d58a4c171027c318ea4.png] #### 1.3.2 变量 #### * Lua 是弱类型语言,变量无需类型声明即可直接使用。变量分为全局变量与局部变量。 * Lua 中的变量默认都是全局变量,即使声明在语句块或函数里。全局变量一旦声明,在当前文件中的任何地方都可访问。局部变量 local 相当于 Java 中的 private 变量,只能在声明的语句块中使用。 #### 1.3.3 动态类型 #### * Lua 是动态类型语言,变量的类型可以随时改变,无需声明。 ### 1.3.3 运算符 ### * 运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。 * Lua 提供了以下几种运算符类型: * 算术运算符 * 关系运算符 * 逻辑运算符 * 其他运算符 #### 1.3.4 算术运算符 #### * 设定 A 的值为 10,B 的值为 20 ![在这里插入图片描述][54a56f3921be428c9de29d90143c9ea2.png] > * SciTE 对 Lua 支持的目前最高版本为 5.1,而整除运算符//需要在 Lua5.3 版本以上,所以当前 SciTE 中无法看到效果。 #### 1.3.5 关系运算符 #### * 设定 A 的值为 10,B 的值为 20 ![在这里插入图片描述][c2ea029efcdc4b7b9c59025d08c87fde.png] #### 1.3.6 逻辑运算符 #### * **Lua 系统将 false 与 nil 作为假,将 true 与非 nil 作为真,即使是 0 也是真** * 设定 A 的值为 true,B 的值为 false ![在这里插入图片描述][9c12552d78d84870a9a5dc4d1737f6d7.png] #### 1.3.7 其他运算符 #### ![在这里插入图片描述][a2481d11d63b4f2994a3f4288a258ea9.png] ### 1.4 函数 ### * Lua 中函数的定义是以 function 开头,后跟函数名与参数列表,以 end 结尾。 **其可以没有返回值,也可以一次返回多个值。** #### 1.4.1 固定参函数 #### * Lua 中的函数在调用时,其不要求实参的个数必须与函数中形参的个数相同。如果实参个数少于形参个数,则系统自动使用 nil 填充;如果实参个数多于形参个数,多出的将被系统自动忽略。 -- 定义一个普通函数 包含两个形参 function f1(a,b) print(a,b) end print("无实参传递") f1() print("传递一个实参") f1(666) print("传递两个实参") f1(666,888) print("传递三个实参") f1(666,888,999) #### 1.4.2 可变参函数 #### * 在函数定义时不给出具体形参的个数,而是使用三个连续的点号。在函数调用时就可以向该函数传递任意个数的参数,函数可以全部接收。 -- 定义一个普通函数,包含可变参数 function f2(...) local a,b,c,d=... print(...) end print("传递三个参数") f2(10,20,30) print("传递四个参数") f2(10,20,30,40) print("传递五个参数") f2(10,20,30,40,50) #### 1.4.3 返回多个值 #### * Lua 中的函数一次可以返回多个值,但需要有多个变量来同时接收。 -- 定义一个函数,返回两个值 function f3(a,b) local sum=a+b local mul=a*b return sum,mul; end -- 一次性接受两个值 m,n=f3(10,10) print("一个函数返回多个值") print(m,n) #### 1.4.4 函数作为参数 #### * Lua 的函数中,允许函数作为参数。而作为参数的函数,可以是已经定义好的普通函数,也可以是匿名函数。 -- 定义两个普通函数 function sum(m,n) return m+n end function mul(m,n) return m*n end function complex(m,n,fun) local result=fun(m,n) print(result) end -- 普通调用 complex(3,4,sum) complex(3,4,mul) -- 匿名函数调用 complex(3,4, function(a,b) return a-b; end ); ### 1.5 流程控制语句 ### #### 1.5.1 if语句&if 嵌套语句 #### * Lua 提供了 if…then 用于表示条件判断,其中 if 的判断条件可以是任意表达式。 * Lua 中的 if 语句的判断条件可以使用小括号括起来,也可以不使用 a=10 if a>9 then print("num>0") elseif num==0 then print("num = 0") else print("num<0") end * Lua 中提供了专门的关键字 `elseif` 来做 `if` 嵌套语句。注意,不能使用 `else` 与 `if` 两个关键字的联用形式,即不能使用 `else if`来嵌套 `if` 语句 a=10 if a>9 then print("num>0") elseif num==0 then print("num = 0") else print("num<0") end ### 1.6 循环控制语句 ### * Lua 提供了四种循环控制语句:**while…do 循环、repeat…until 循环、数值 for 循环,及泛型 for 循环。** * 同时,Lua 还提供了 break 与 goto 两种循环流程控制语句。 1. `while...do` * 只要 while 中的条件成立就一直循环。 score=450 while score>= 425 do print("过了") score=score-1 end 2. `repeat...until` * until 中的条件成立了,循环就要停止。 score=450 repeat print(score) score=score+1 until score>=427 3. 数值 for * 这种 for 循环只参用于循环变量为数值型的情况。其语法格式为: for var=exp1, exp2, exp3 do --循环体 end for i=10,60,10 do print(i) end * var 为指定的循环变量,exp1 为循环起始值,exp2 为循环结束值,exp3 为循环步长。步长可省略不写,默认为 1。每循环一次,系统内部都会做一次当前循环变量 var 的值与 exp2的比较,如果 var 小于等于 exp2 的值,则继续循环,否则结束循环。 4. 泛型 for * 泛型 for 用于遍历 table 中的所有值,其需要与 Lua 的迭代器联合使用。 -- 遍历emp中所有数组元素 for k,v in ipairs(emp) do print(k,v) end 5. break * break 语句可以提前终止循环。其只能用于循环之中 6. goto * goto 语句可以将执行流程无条件地跳转到指定的标记语句处开始执行。当然,该标识语句在第一次经过时也是会执行的,并非是必须由 goto 跳转时才执行。 * 语句标记使用一对双冒号括起来,置于语句前面。goto 语句可以使用在循环之外。 > * Lua5.1 中不支持双冒号的语句标记。 ## 二 Lua 语法进阶 ## ### 2.1 table ### 1. 数组 * 使用 table 可以定义一维、二维、多维数组。 * Lua 中的数组索引是从 1开始的,且无需声明数组长度,可以随时增加元素。同一数组中的元素可以是任意类型。 -- 声明一个空的数组 arr={ } for i=1,3 do -- 声明空数组 arr[i]={ } for j=1,3 do arr[i][j]=i*j end end for i=1,3 do for j=1,3 do print(arr[i][j]) end end 2. map * 使用 table 也可以定义出类似 map 的 key-value 数据结构。其可以定义 table 时直接指定key-value,也可单独指定 key-value。而访问时,一般都是通过 table 的 key 直接访问,也可以数组索引方式来访问,此时的 key 即为索引。 -- 定义一个map emp={ name="张三",age=23,depart="销售"} -- 通过下标方式操作 emp["gender"]="男" print(emp["name"]) print(emp["gender"]) emp.workAge=3 print(emp.workAge) print(emp.name) 3. 混合结构 * Lua 允许将数组与 key-value 混合在同一个 table 中进行定义。 * key-value 不会占用数组的数字索引值。 -- 定义一个数组,map混合结构 emps={ { name="001",age=11}, { name="002",age=12}, { name="003",age=13}, { name="004",age=14} } for i=1,4 do print(emps[i].name..":"..emps[i].age) end ### 2.2 table 操作函数 ### * Lua 中提供了对 table 进行操作的函数 -------------------- * `table.concat()` table.concat (table [, sep [, start [, end]]]): * 该函数用于将指定的 table 数组元素进行字符串连接。连接从 start 索引位置到 end索引位置的所有数组元素, 元素间使用指定的分隔符 sep 隔开。 * 如果 table 是一个混合结构,那么这个连接与 key-value 无关,仅是连接数组元素。 -------------------- * `table.unpack()` table.unpack (table [, i [, j]]) * 拆包。该函数返回指定 table 的数组中的从第 i 个元素到第 j 个元素值。i 与 j 是可选的,默认 i 为 1,j 为数组的最后一个元素。 * **Lua5.1 不支持该函数** -------------------- * `table.pack()` table.pack (...) * 打包。该函数的参数是一个可变参,其可将指定的参数打包为一个 table 返回。这个返回的 table 中具有一个属性 n,用于表示该 table 包含的元素个数。 * **Lua5.1 不支持该函数** -------------------- * `table.maxn()` table.maxn(table) * 该函数返回指定 table 的数组中的最大索引值,即数组包含元素的个数。 -------------------- * `table.insert()` table.insert (table, [pos,] value) * 该函数用于在指定 table 的数组部分指定位置 pos 插入值为 value 的一个元素。其后的元素会被后移。pos 参数可选,默认为数组部分末尾。 -------------------- * `table.remove ()` table.remove (table [, pos]) * 该函数用于删除并返回指定 table 中数组部分位于 pos 位置的元素。其后的元素会被前移。pos 参数可选,默认删除数组中的最后一个元素。 -------------------- * `table.sort()` table.sort(table [,fun(a,b)]) * 该函数用于对指定的 table 的数组元素进行升序排序,也可按照指定函数 fun(a,b)中指定的规则进行排序。fun(a,b)是一个用于比较 a 与 b 的函数,a 与 b 分别代表数组中的两个相邻元素。 > * 如果 arr 中的元素既有字符串又有数值型,那么对其进行排序会报错。 > * 如果数组中多个元素相同,则其相同的多个元素的排序结果不确定,即这些元素的索引顺序不确定。 > * 如果数组元素中包含 nil,则排序会报错。 ### 2.3 迭代器 ### * Lua 提供了两个迭代器 `pairs(table)`与 `ipairs(table)`。这两个迭代器通常会应用于泛型 for循环中,用于遍历指定的 table。这两个迭代器的不同是: * **`ipairs(table)`:仅会迭代指定 table 中的数组元素。** * **`pairs(table)`:会迭代整个 table 元素,无论是数组元素,还是 key-value。** emp={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} print(table.concat(emp,",",2)) print(table.concat(emp,",",2,3)) -- 遍历emp中所有数组元素 for k,v in ipairs(emp) do print(k,v) end -- 遍历emp中所有的元素 for k,v in pairs(emp) do print(k,v) end ### 2.4 模块 ### * 模块是Lua中特有的一种数据结构。从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。 * 模块文件主要由 table 组成。在 table 中添加相应的变量、函数,最后文件返回该 table即可。如果其它文件中需要使用该模块,只需通过 require 将该模块导入即可。 1. 定义一个模块 * 模块是一个 lua 文件,其中会包含一个 table。一般情况下该文件名与该 table 名称相同,但其并不是必须的。 * 定义模块`rectangle.lua` -- 声明一个模块 rectangle={ } --为模块添加一个变量 rectangle.pi = 3.14 --为模块添加函数(求周长) function rectangle.perimeter(a, b) return (a+b)* 2 end --以匿名函数方式为模块添加一个函数(求面积) rectangle.area = function(a,b) return a*b end --定义与模块无关的内容 -- 定义一个全局变量 goldenRatio=0.618 --定义一个局部函数(求圆的面积) local function circularArea(r) return rectangle.pi*r*r end --定义一个全局函数(求矩形中最大圆的面积) function maxcircularArea(a,b) local r=math.min(a,b) return circularArea(r) end return rectangle 2. 使用模块 * 用函数 require(“文件路径”),【文件名是不能写.lua 扩展名】该函数可以将指定的 lua 文件静态导入(合并为一个文件)。 * 注意:该函数的使用可以省略小括号,写为 require “文件路径”。 * require()函数是有返回值的,返回的就是模块文件最后 return 的 table。可以使用一个变量接收该 table 值作为模块的别名,就可以使用别名来访问模块了。 -- 导入模块 rect=require "rectangle" -- 访问模块的属性,调用模块的函数 print(rectangle.pi) print(rectangle.perimeter(3,5)) print(rectangle.area(3,5)) print(rect.perimeter(3,5)) print(rect.area(3,5)) 3. 再看模块 * 模块文件中一般定义的变量与函数都是模块 table 相关内容,但也可以定义其它与 table无关的内容。这些全局变量与函数就是普通的全局变量与函数,与模块无关,但会随着模块的导入而同时导入。所以在使用时可以直接使用,而无需也不能添加模块名称。 -- 访问模块中与模块无关的内容 print(goldenRatio) print(maxcircularArea(3,5)) ### 2.5 元表与元方法 ### * 元数据:描述数据的数据(如:数据库名、表名、字段名) * 元数据表:描述表的另外的表(如:表A由表B描述) * 元表,即 **Lua 中普通 table 的元数据表,而元方法则是元表中定义的普通表的默认行为。** Lua 中的每个普通 table 都可为其定义一个元表,用于扩展该普通 table 的行为功能。 * 例如,对于 table 与数值相加的行为,Lua 中是没有定义的,但用户可通过为其指定元表来扩展这种行为;再如,用户访问不存在的 table 元素,Lua 默认返回的是 nil,但用户可能并不知道发生了什么。此时可以通过为该 table 指定元表来扩展该行为:给用户提示信息,并返回用户指定的值 #### 2.5.1 重要函数和方法 #### * 元表中有两个重要函数: * `setmetatable(table,metatable)`:将 metatable 指定为普通表 table 的元表。 * `getmetatable(table)`:获取指定普通表 table 的元表。 1. \_ \_index 元方法 * 当用户在对 table 进行读取访问时,如果访问的数组索引或 key 不存在,那么系统就会自动调用元表的`_ _index` 元方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的`_ _index` 元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回 `nil`。 -- 以函数方式重写__index方法 emp={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} print(emp.x) --nil --声明一个元表 meta ={ }; --将原始表与元表相关联 setmetatable(emp , meta) --有返回值的情况 meta._index = function(tab,key) return (key.."值不存在") end --无返回值的情况 meta.__index =function(tab, key) print("通过["..key.."]访问的值不存在") end print(emp.x) --nil print(emp[2]) --banana emp2={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} print(emp2[5]) --nil --声明一个元表 meta2={ } --将原始表与元表相关联 setmetatable(emp2,meta2) --再定义一个普通表 others={ } others[5]="strawberry" others[6]="ananas" -- 指定元表为另一个普通表 meta2.__index=others --在原始表中若找不到,则会到元表指定的普通表中查找 print(emp2[5]) --strawberry 2. \_ \_newindex 元方法 * 当用户为 table 中一个不存在的索引或 key 赋值时,就会自动调用元表的`_ _newindex` 元方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的`_ _newindex` 元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回 `nil`。 emp3={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} print(emp3[5]) --nil --声明一个元表 meta3={ } --将原始表与元表相关联 setmetatable(emp3,meta3) -- 无返回值的情况 function meta3.__newindex(tab,key,value) print(key..":"..value) -- 将新增的key-value写入到原始表 rawset(tab,key,value) end emp3.x="ming" print(emp3.x) -- ming emp4={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} print(emp4[5]) --nil --声明一个元表 meta4={ } --将原始表与元表相关联 setmetatable(emp4,meta4) -- 定义一个普通表 other4={ } -- 元表指定额另一个普通表的作用是,暂存新增的数据 meta4.__newindex=other4 emp4.x="ming" print(emp4.x) --nil print(other4.x) --ming 3. 运算符元方法 * 如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的元方法。 * 例如,如果要为一个 table 扩展加号(+)运算功能,则可重写该 table 元表的`_ _add`元方法,而具体的运算规则,则是定义在该重写的元方法中的。这样,当一个 table 在进行加法(+)运算时,就会自动调用其元表的`_ _add` 元方法 -- 运算符元方法 emp5={ "apple",type="fruit",health="true",yieldly="east","banana","tangerine","grape"} -- 声明一个元表 meta5={ __add=function(tab,num) -- 遍历tab中的所有元素 for k,v in pairs(tab) do --若value为数值类型,做算数运算 if type(v)=="number" then tab[k]=v+num --若value为string,做字符拼接 elseif type(v)=="string" then tab[k]=v..num end end -- 返回变化后的table return tab end }; --将原始表与元表相关联 setmetatable(emp5,meta5) emp5=emp5+5 for k,v in pairs(emp5) do print(k.." : "..v) end * 其它操作 <table> <thead> <tr> <th>元方法</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td>__add</td> <td>加法,+</td> </tr> <tr> <td>__sub</td> <td>减法,-</td> </tr> <tr> <td>__mul</td> <td>乘法,*</td> </tr> <tr> <td>__div</td> <td>除法,/</td> </tr> <tr> <td>__mod</td> <td>取模,%</td> </tr> <tr> <td>__pow</td> <td>次幂,^</td> </tr> <tr> <td>__unm</td> <td>取反,-</td> </tr> <tr> <td>__idiv</td> <td>取整除法,//</td> </tr> <tr> <td>__lt</td> <td>小于,<</td> </tr> <tr> <td>__concat</td> <td>字符串连接,…</td> </tr> <tr> <td>__len</td> <td>字符串长度,#</td> </tr> <tr> <td>__band</td> <td>按位与,&</td> </tr> <tr> <td>__bor</td> <td>按位或,</td> </tr> <tr> <td>__bxor</td> <td>按位异或,~</td> </tr> <tr> <td>__bnot</td> <td>按位非,~</td> </tr> <tr> <td>__shl</td> <td>按位左移,<<</td> </tr> <tr> <td>__shr</td> <td>按位右移,>></td> </tr> <tr> <td>__eq</td> <td>等于,==</td> </tr> <tr> <td>__le</td> <td>小于等于,<=</td> </tr> </tbody> </table> 1. \_ \_tostring 元方法 * 直接输出一个 table,其输出的内容为类型与 `table` 的存放地址。如果想让其输出 table中的内容,可重写`_ _tostring` 元方法 -- __tostring方法 emp6={ "apple",type="fruit",health=1,yieldly="east","banana","tangerine","grape"} -- 声明一个元表 meta6={ __add=function(tab,num) -- 遍历tab中的所有元素 for k,v in pairs(tab) do --若value为数值类型,做算数运算 if type(v)=="number" then tab[k]=v+num --若value为string,做字符拼接 elseif type(v)=="string" then tab[k]=v..num end end -- 返回变化后的table return tab end, -- 这里需要加上一个逗号 __tostring=function(tab) str="" --字符串拼接 for k,v in pairs(emp6) do str=str.." "..k..":"..v end return str end }; -- 将原始表与元表相关联 setmetatable(emp6,meta6) emp6=emp6+10 print(emp6) 2. \_ \_call 元方法 * 当将一个 table 以函数形式来使用时,系统会自动调用重写的`_ _call` 元方法。该用法主要是可以简化对 table 的相关操作,将对 table 的操作与函数直接相结合。 -- __call方法 emp7={ "apple",type="fruit",health=1,yieldly="east","banana","tangerine","grape"} -- 将原始表与元表相关联 匿名函数 setmetatable(emp7,{ __call=function(tab,num,str) -- 遍历table for k,v in pairs(tab) do --若value为数值类型,做算数运算 if type(v)=="number" then tab[k]=v+num --若value为string,做字符拼接 elseif type(v)=="string" then tab[k]=v..num end end return tab end }) newemp=emp7(5,"-hello") for k,v in pairs(newemp) do print(k..":"..v) end 3. 元表单独定义 * 为了便于管理与复用,可以将元表单独定义为一个文件。该文件中仅可定义一个元表,且一般文件名与元表名称相同。 * 若一个文件要使用其它文件中定义的元表,只需使用 require “元表文件名”即可将元表导入使用。 * 如果用户想扩展该元表而又不想修改元表文件,则可在用户自己文件中重写其相应功能的元方法即可。 ### 2.6 面向对象 ### 1. 简单对象的创建 * Lua 中通过 table 与 fcuntion 可以创建出一个简单的 Lua 对象:table 为 Lua 对象赋予属性,通过 function 为 Lua 对象赋予行为,即方法 -- 简单对象的创建 -- 创建一个animal对象 animal={ name="Tom",age=3, say=function(voice) print(animal.name.."正在"..voice.."叫") end } -- 添加方法 animal.run=function(speed) print(animal.name.."正在以"..speed.."m/s前进") end animal.say("喵喵") animal.run(5) -- 创建对象的指针,观察是否指向同一个对象 animal2=animal -- 打印对象的名字和地址 print(animal2.name, animal2) print(animal.name, animal) -- self代表当前的实例 -- 冒号中会自动包含一个self参数,相当于this function animal:play(games) print(self.name.."正在玩"..games.."游戏") end -- 将animal置空 animal=nil animal2.name="philips" animal2:play("捉迷藏") 2. 类的创建 * Lua 中使用 table、function 与元表可以定义出类:使用一个表作为基础类,使用一个function 作为该基础类的 new()方法。在该 new()方法中创建一个空表,再为该空表指定一个元表。该元表重写`_ _index` 元方法,且将基础表指定为重写的`_ _index`元方法。由于 new()中的表是空表,所以用户访问的所有 key 都会从基础类(表)中查找。 -- 创建类 Animal={ name="no_name",age=1} function Animal:bark(voice) print(self.name..voice.."叫") end -- 为该类添加一个无参构造器 function Animal:new() -- 创建一个空表 local a={ } -- 为新表指定元素为当前基类表 -- 如果后续操作,没有在a表中找到相关的数据,就从Animal表中查找,如果Animal中也没有就返回空 setmetatable(a,{ __index=self}) -- 返回新表 return a end animal01=Animal:new() animal02=Animal:new() print(animal01) print(animal02) -- 给表a属性赋值 animal01.name="TOM" animal01.age=3 animal01.type="猫" -- print(animal01.name.."是一只"..animal01.age.."岁的"..animal01.type) function animal02:skill() return "我会捉老鼠" end print(animal02.name..animal02.age.."岁,"..animal02.skill()) -- 类的继承 BaseAnimal={ name="no_name",age=1} function BaseAnimal:bark(voice) print(self.name..voice.."叫") end -- 为该类添加一个无参构造器 function BaseAnimal:new() -- 创建一个空表 local a={ } -- 为新表指定元素为当前基类表 -- 如果后续操作,没有在a表中找到相关的数据,就从Animal表中查找, --如果Animal中也没有就返回空 setmetatable(a,{ __index=self}) -- 返回新表 return a end -- 为该类添加一个带参构造器 function BaseAnimal:new(obj) local a=obj or { } setmetatable(a,{ __index=self}) -- 返回新表 return a end animal03=BaseAnimal:new() -- 调用带参构造器 实参为一个匿名表 animal04=BaseAnimal:new({ type="猫"}) animal04.name="jerry" animal04.age=3 print(animal04.name.." : "..animal04.age.."岁,"..animal04.type) --为Animal类创建一个子类cat cat = BaseAnimal:new({ type="波斯猫"}) cat.eyes ="蓝眼睛" tomcat = cat : new() tomcat.name = "Tom" print(tomcat.name.."是"..tomcat.eyes.."的"..tomcat.type) ### 2.7 协同线程 ### * Lua 中有一种特殊的线程,称为 coroutine,协同线程,简称协程。其可以在运行时暂停执行,然后转去执行其它线程,然后还可返回再继续执行没有执行完毕的内容。即可以“走走停停,停停再走走”。 * 协同线程也称为协作多线程,在 Lua 中表示独立的执行线程。任意时刻只会有一个协程执行,而不会出现多个协程同时执行的情况。 * 协同线程的类型为 thread,其启动、暂停、重启等,都需要通过函数来控制。 * 用于控制协同线程的基本方法。 ![在这里插入图片描述][bac4162c9be541ea9d039135c0240138.png] -- 协同线程 -- 创建一个协同线程实例 crt=coroutine.create( function(a,b) print(a,b,a+b) -- 获取正在运行的协同线程的实例,thread类型 tr=coroutine.running(); print(tr) -- 查看协同线程实例的类型 print(type(tr)) -- 查看系统线程实例的状态 print(coroutine.status(crt)) -- 将当前协同线程实例挂起 coroutine.yield() print("线程又重新返回到了协同线程") end ) -- 启动协同线程实例 coroutine.resume(crt,3,5) -- 查看crt类型 print("main-"..type(crt)) -- 查看协同线程实例的状态 print("main-"..coroutine.status(crt)) --继续执行协同线程 coroutine .resume (crt, 3,5) --查看协同线程实例的状态 print ( "main-"..coroutine.status (crt)) -- 创建一个协同线程实例 crt2=coroutine.create( function(a,b) print(a,b) -- 将当前线程挂起,同时携带两个返回值 coroutine.yield(a*b,a/b) print("线程又重新返回到了协同线程") -- 返回两个值 return a+b,a-b end ) --启动协同线程,返回三个值 --第一个返回值为协同线程启动成功状态 --后面的返回值为内置函数的返回值 success1, resu1t1,result2 = coroutine.resume(crt2,12,3) print(success1,resu1t1,result2) success2,result1,result2 = coroutine.resume(crt2) print(success2,result1,result2) ### 2.8 协同函数 ### * 协同线程可以单独创建执行,也可以通过协同函数的调用启动执行。使用 coroutine 的wrap()函数创建的就是协同函数,其类型为 function。 * 由于协同函数的本质就是函数,所以协同函数的调用方式就是标准的函数调用方式。只不过,协同函数的调用会启动其内置的协同线程。 -- 协同函数 --创建一个协同函数 cf =coroutine.wrap( function(a,b) print(a,b) -- 获取当前协同函数创建的协同线程 tr=coroutine.running() print("tr的类型为:"..type(tr)) -- 挂起当前的协同线程 -- 也可以添加返回的参数,如返回协同线程的实例 -- coroutine.yield(coroutine.running(),a+1,b+1) coroutine.yield(a+1,b+1) print("线程又重新返回到了协同线程") return a+b,a*b end ) -- 调用协同函数,启动协同线程 result1,result2=cf(3,5) print(result1,result2) print("cf的类型为:"..type(cf)) -- 重启挂起的协同线程 result1,result2=cf(3,5) print(result1,result2) --[[ -- 调用协同函数,启动协同线程 cftr,result1,result2=cf(3,5) -- 重启挂起的协同线程 success,result1,result2=coroutine.resume(cftr) ]]-- ### 2.9 文件IO ### * Lua 中提供了大量对文件进行 IO 操作的函数。这些函数分为两类:静态函数与实例函数。所谓静态函数是指通过 io.xxx()方式对文件进行操作的函数,而实例函数则是通过 Lua中面向对象方式操作的函数。 #### 2.9.1 常用静态函数 #### 1. io.open() io.open (filename [, mode]) * 以指定模式打开指定文件,返回要打开文件的句柄,就是一个对象。 * 其中模式 mode 有三种,但同时还可配合两个符号使用: * r:只读,默认模式 * w:只写,写入内容会覆盖文件原有内容 * a:只写,以追加方式写入内容 * \+:增加符,在 r+、w+、a+均变为了读写 * b:二进制表示符。如果要操作的文件为二进制文件,则需要变为 rb、wb、ab。 2. io.input() io.input (file) * 指定要读取的文件。 3. io.output() io.output (file) * 指定要写入的文件。 4. io.read() io.read([format]) * 以指定格式读取 io.input()中指定的输入文件。其中 format 格式有: * \*l:从当前位置的下一个位置开始读取整个行,默认格式 * \*n:读取下一个数字,其将作为浮点数或整数 * \*a:从当前位置的下一个位置开始读取整个文件 * number:这是一个数字,表示要读取的字符的个数 5. io.write() io.write(data) * 将指定的数据 data 写入到 io.output()中指定的输出文件。 #### 2.9.2 常用实例函数 #### 1. file:read() * 这里的 file 使用的是 io.open()函数返回的 file,其实际就是 Lua 中的一个对象。其用法与 io.read()的相同。 2. file:write() * 用法与 io.write()的相同。 3. file:seek() file:seek ([whence [, offset]]) * 该函数用于获取或设置文件读写指针的当前位置。位置从 1 开始计数,除文件最后一行外,每行都有行结束符,其会占两个字符位置。位置 0 表示文件第一个位置的前面位置。 * 当 seek()为无参时会返回读写指针的当前位置。参数 whence 的值有三种,表示将指针定位的不同位置。而 offset 则表示相对于 whence 指定位置的偏移量,offset 的默认值为 0,为正表示向后偏移,为负表示向前偏移。 * set:表示将指针定位到文件开头处,即 0 位置处 * cur:表示指针保持当前位置不变,默认值 * end:表示将指针定位到文件结尾处 [2d84253726fb4d58a4c171027c318ea4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/73fc278a97bb4040a8643cd39e8e62b9.png [54a56f3921be428c9de29d90143c9ea2.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/6453730040634a52954731aafd6767ed.png [c2ea029efcdc4b7b9c59025d08c87fde.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/e3925f8c642a47c6b39440cbdd9cb170.png [9c12552d78d84870a9a5dc4d1737f6d7.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/e254c5f8da294c308acff69562bb0429.png [a2481d11d63b4f2994a3f4288a258ea9.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/16aee9b0746a4a898b81d2741f87859d.png [bac4162c9be541ea9d039135c0240138.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/25/7d4383c9a21244c5bf4adb2f55b93b81.png
还没有评论,来说两句吧...