lua是的小巧的脚本语言,它的设计目的是为了可以嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能。javascript
先执行百度安装好 lua,本文是采用 vscode 运行的,windows 用户可使用lua 自带的编辑器。php
-- 这是单行注释 --[[ 这里是多行注释 注释 --]]
ua 是动态类型语言,变量不要类型定义,只须要为变量赋值。 值能够存储在变量中,做为参数传递或结果返回。java
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。python
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中至关于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)实际上是一个"关联数组"(associative arrays),数组的索引能够是数字、字符串或表类型。在 Lua 里,table 的建立是经过"构造表达式"来完成,最简单构造表达式是{},用来建立一个空表。 |
操做符 | 描述 | 实例 |
---|---|---|
+ | 加法 | A + B 输出结果 30 |
- | 减法 | A - B 输出结果 -10 |
* | 乘法 | A * B 输出结果 200 |
/ | 除法 | B / A w输出结果 2 |
% | 取余 | B % A 输出结果 0 |
^ | 乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果 -10 |
bool = nil -- 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中至关于false)。 id = 1 -- 声明整数 amount = 1.2 -- 声明整数(lua 整数和小数都叫作整数) name = 'jacky' -- 声明字符串,单引号双引号一个意思,都是字符串 nickName = "哈基石" -- 声明字符串 local age = 18 -- 声明局部变量,带有 local 的是局部定义,不带有的是全局定义 print(id..name) -- 链接两个变量或字符相似 php 中的 . 和 javascript 中的 +
主要是先熟悉一下 Lua 的语法结构,你不用记后面会大部分用到,有一点印象便可。golang
bool = true -- 定义全局变量 if bool == true then print(true) -- 输出 true end if 0 then print(true) -- 输出 true end if nil then print(true) -- 不输出 重点记忆 end if bool == true then print(true) -- 输出 true else print(false) end local num = 3 if num == 1 then print(1) elseif num == 2 then print(2) else print(3) -- 输出3 end
其实 table 应该放后面学的,但总以为放后面会致使知识很零散,没有办法高效学习。学会了 table,本文基本上60%就已经学完了。sql
相对于其余语言而言,lua 不须要学习那么多结构像数组,结构体,map,切片之类的。都说 php 的数组一招鲜走遍天下,啥都能干。windows
可是 lua更恐怖,有着更强大的功能呢,包括数组,类,匿名...数组
lua 的数字键值是从1开始的,这一点须要特别注意。数据结构
数字做为 key 的数组闭包
--建立一个空的 table tab = {} -- 直接初始化 table tab = {"java", "golang", "lua", "python"} -- 数字下标访问 print(tab[1]) -- 输出 java print(tab[2]) -- 输出 golangtabl tab[1] = "C#" -- 赋值 tab[7] = "C++" -- 赋值 print(tab[1]) -- 输出 C# print(tab[7]) -- 输出 C++ tab[1] = nil -- 删除数组中的元素, key 保留值为 nil print(tab[1]) -- 输出 nil print(tab[1000]) -- 输出 nil 不会报错哦
字符串做为 key 的数组
tab = {beijing="北京", shanghai="上海", chengdu="成都"} tab["hangzhou"] = "杭州" print(tab.beijing, tab.hangzhou, tab["shanghai"]) -- 输出 北京 杭州 上海
总结: 若是是数字做为索引,那么须要用[]
来访问,若是字符串做为索引,那么可使用[], 和 xx.xxx
两种方式均可以访问和修改
table的增长
-- 新增元素到 tab 的末尾 tab = {"beijing", "shanghai"} table.insert(tab, "hangzhou") -- 将广州写入tab的末尾,下标就是3 print(tab[3]) -- hangzhou -- 新增到指定的位置 table.insert(tab, 1, "neimeng") -- 插入到1的位置,其原来位置后面的元素向后偏移 print(tab[1], tab[2]) -- 输出 neimeng beijing
table 的删除
tab = {"java", "C++", "golang", "sql"} res = table.remove(tab, 2) -- 删除第二个元素,C++,其余元素会进行补位(向前偏移) print(res, tab[1], tab[2]) -- 输出C++ java golang
多维 table 与一位多了一层,使用状况彻底同样。
tab2 = {{}} -- 定义个空的二维 tab tab2 = {{"北京", "上海"}, {"java", "go"}} print(tab2[1][1], tab2[2][2]) -- 输出 北京 go
元表( metatable
)是一个表,它是使用键集和相关元方法来修改附加到的表的行为。这些元方法是强大的Lua功能。
__index
这是 metatable 最经常使用的键。当你经过键来访问 table 的时候,若是这个键没有值,那么Lua就会寻找该table的metatable,是否存在__index 方法。若是存在就会调用它,不存在就会返回 nil
__newindex
若是对 table 里面的数据进行新增,例如 table["xxx"] = 300
那么就会调用 metatable __newindex
方法__call
若是对 table 直接调用,那么会调用 metatable 中的 __call
这个方法, 例如调用tab()
。__tostring
若是堆 table 直接输出,那么会调用 metatable 中的__tostring
这个方法,例如print(tab)
-- __index 索引 tab = {10, 20, 30} tab2 = {} -- __newindex 赋值方式1 metatab = { -- __index 若是tab中不存在key,则调用__index。 __index = function(t, k) print(string.format( "调用了 tab 不存在的索引,key=%s", k)) end, -- __newindex = tab2, -- 赋值方式1,直接赋值給 tab2 例如调用 xxx["user"]=1 ,那么tab2["user"]的值为1 __newindex = function(t, k, v) -- 赋值方式2方法自定义调用 rawset(t, k, v) -- 赋值給 t print(string.format( "新增了tab不存在的索引,k=%s, v=%s", k, v)) end, __call = function(t, arg1, arg2) print(string.format("调用了__call, arg1=%s, arg2=%s,返回了true", arg1, arg2)) end, __tostring = function(t) print("调用了直接输出 __tostring") -- 直接输出 tab 会调用这个方法 return "call __tostring" end, __le = function(t, newtab) --两个表调用了 <= 比较运算符 print("调用了两个表==运算符") return true end, __add = function(t, newtab) -- 根据本身的场景来处理,这里处理的是两个表合并 print("调用了__add") for k, v in pairs(newtab) do -- 尚未学习 for,先不要关心 table.insert(t, v) end return t end, } -- 对指定 table 设置元表(metatable) restab = setmetatable(tab, metatab) restab[20] = 2000 -- 调用 __index 方法 restab(32, 45) -- 调用 __call 方法 print(restab) -- 调用 __tostring newtab = {80, 90} res = restab + newtab -- 调用两个表想加 if restab <= n then -- 调用了 __le -- do something end
操做运算符
模式 | 描述 |
---|---|
__add | 对应的运算符 '+'. |
__sub | 对应的运算符 '-'. |
__mul | 对应的运算符 '*'. |
__div | 对应的运算符 '/'. |
__mod | 对应的运算符 '%'. |
__unm | 对应的运算符 '-'. |
__concat | 对应的运算符 '..'. |
__eq | 对应的运算符 '=='. |
__lt | 对应的运算符 '<'. |
__le | 对应的运算符 '<='. |
好吧,原表终于学完了,若是你在学习中也进行实际操做,那么必定超过30分钟了。不过坚持一下,剩下的部分很快了。有了元表的基础就能够学习面向对象了
Lua 的循环有3中分别是 for, while, repeat unitl。其中 for 包括基本 for 与泛型 for,至关于其余语言的 for in 或者 foreach。
-- 最简单的循环 a = 5 while a >= 0 do print(a) -- 输出 5 4 3 2 1 0 a = a -1 end
for 循环分为两种,数值型和泛型,数值型比其余语言更简介,也有点怪怪的,熟悉就好。
数值型的 for 循环长这样
for var=exp1,exp2,exp3 do <执行体> end
exp1表明初始化的数值,exp2表明结束条件,注意这里没有比较运算符,exp3表明步长,也能够省略。
for i = 1, 5, 1 do print(i) -- 输出 1 2 3 4 5 end -- 步长是2的状况 for i = 1, 5, 2 do print(i) -- 输出 1 3 5 end
步长也就是 exp3能够省略,默认步长是1
for i = 1, 5 do print(i) -- 输出 1 2 3 4 5 end
这里须要注意一点,与其余语言不同 for的三个表达式在循环开始前一次性求值,之后再也不进行求值。好比上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
泛型循环有两个迭代函数 pairs 与 ipairs,他们的区别是 ipairs遇到nil会中止,pairs会输出nil值而后继续下去,文字看不动不要紧,看代码。
table = {"1", "2", "3", nil, "5"} for k, v in pairs(table) do print("k="..k..",val="..v) end
这个栗子会输出
k=1,val=1 k=2,val=2 k=3,val=3 k=5,val=5
-- ipairs 若是文字没有理解啥意思,能够对比一下上一个栗子与这个栗子的结果 table = {"1", "2", "3", nil, "5"} for k, v in ipairs(table) do print("k="..k..",val="..v) end
这个栗子会输出
k=1,val=1 k=2,val=2 k=3,val=3
-- repeat a = 1 repeat -- 开始重复 print(a) a = a +1 until(a>3) -- 条件 直到条件判断语句(condition)为 true 才会中止执行。
这个栗子会输出
1 2 3
趁热打铁,迭代器与 for 循环一块儿结合着理解会很好理解。其实上个例子中 pairs 与 ipairs 就是两个泛型迭代器,lua 内部都给咱们实现好了。
迭代器(iterator)是一种对象,它可以用来遍历标准模板库容器中的部分或所有元素,每一个迭代器对象表明容器中的肯定的地址。
在 Lua 中迭代器是一种支持指针类型的结构,它能够遍历集合的每个元素。
泛型 for 在本身内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
无状态的迭代器是指不保留任何状态的迭代器,所以在循环中咱们能够利用无状态迭代器避免建立闭包花费额外的代价。
-- 迭代函数 state 只有在初始化的时候赋值 function square(max, add) if add >= max then -- 当累加器到达最大返回 nil 外部会跳出循环 return nil else add = add+1 -- lua 没有 ++ return add, add*add -- 返回两个参数与 go 类似,返回当前的值和乘 end end for i, j in square , 5, 0 do -- 调用自定义迭代器 print(i, j) end
这个栗子输出
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81
不少状况下,迭代器须要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将全部的状态信息封装到 table 内,将 table 做为迭代器的状态常量,由于这种状况下能够将全部的信息存放在 table 内,因此迭代函数一般不须要第二个参数
-- 多状态迭代器 tab = {"google", "baidu"} function square (con) local index = 0 local count = #con -- #表明计算长度 return function() -- 这是一个匿名函数会直接调用 index = index + 1 if index <= count then -- index 小于总长度 return con[index] -- 返回这个 val end end end for val in square(tab) do print(val) -- 输出 google baidu end
支持返回多个返回值
function op(key, val) return key, val -- 支持两个参数返回 end arg1, arg2 = op("user", 100)
参数支持传递函数
-- 参数函数传递的方式 function sum(a, b, func) return func(a, b) end function func(a, b) return a + b end val = sum(10, 20, func) print(val) -- 输出30
匿名函数的方式传递
function max(a, b, func) max = func(a, b) print(max) -- 输出 39 end getmax = function(a, b) if a > b then return a else return b end end max(10, 39, getmax)
可变参数
function args(...) local arg = {...} -- 局部定义不然会有重复赋值 print("总共传入 " .. select("#",...) .. " 个数") print(arg[1], arg[2], arg[3]) end args(10, 20, 30)
这个栗子会输出
总共传入 3 个数 10 20 30
.
的定义定义方式,不能直接使用 self
-- table 引用类型 phone = { name = "马化腾", memory = "128GB" } phone.call = function(self) str = string.format( "%s 正在打电话", self.name) print(str) end phone.call(phone) -- 输出 马化腾 正在打电话
:
的定义方式
phone = { name = "马化腾", pname = "李彦宏" } function phone:call() str = string.format( "%s 正在打电话", self.name) print(str) end function phone:say(name) self.pname = name str = string.format( "%s 说%s你赶忙充钱", self.name, self.pname) print(str) end phone:call() phone:say("马云")
输出
马化腾 正在打电话 马化腾 说马云你赶忙充钱
完整的面向对象实例
-- table 引用类型 phone = { name = "马化腾", } function phone:call() str = string.format("%s 正在打电话", self.name) print(str) end function phone:new(sel) sel = sel or {} -- 因为 table 是引用类型,这里须要从新赋值 setmetatable(sel, {__index = self}) -- 将 self 赋给 sel return sel end mahuat = phone:new() mayun = phone:new() mahuat.name = "马化腾" mayun.name = "马云" mahuat:call() -- 注意 :call()的形式,不然没法得到到 self mayun:call()
输出
马化腾 正在打电话 马云 正在打电话
看代码,认真看,没那么繁琐
Phone = { name = "", } function Phone:call() -- 定义基本方法 str = string.format("%s 正在打电话", self.name) print(str) end function Phone:new(sel) sel = sel or {} setmetatable(sel, {__index = self}) -- 将 self 赋给 sel return sel end -- Mahuat继承了 Phone 方法 Mahuat = Phone:new() function Mahuat:say() self.name = "马化腾" self:call() -- 调用父类的方法 str = string.format("%s 说赶忙充钱!", self.name) print(str) end function Mahuat:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end Mahuat:say() -- 调用本身的方法 -- Mayun继承了 Phone 方法 Mayun = Phone:new() function Mayun:say() self.name = "马云" self:call() -- 调用父类的方法 str = string.format("%s 说快还花呗!", self.name) print(str) end function Mayun:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end Mayun:say() -- 调用本身的方法 -- 派生重写 liyanhong = Phone:new() function liyanhong:call() print("咱们不生产假药!咱们只是假药的搬运工~ ") end -- 重写了 call 方法 function liyanhong:say() self.name = "李彦宏" self:call() -- 调用父类的方法 str = string.format("%说欧耶!", self.name) print(str) end function liyanhong:new(sel) sel = sel or Phone:new() setmetatable(sel, self) self.__index = self return sel end liyanhong:say() -- 调用本身的方法
这个例子输出
马化腾 正在打电话 马化腾 说赶忙充钱! 马云 正在打电话 马云 说快还花呗! 咱们不生产假药!咱们只是假药的搬运工~ 李彦宏 说欧耶!
恭喜您读到这里,实际上尚未完!抱歉了,😄
Lua 的最基础部分就学完了,还须要一个篇写其余的操做,例如文本操做,函数库调用。