当前位置:   article > 正文

Lua语法

Lua语法


内容转自b站up主唐老狮

环境搭建以及第一个lua程序

环境安装

luaForWindows
环境下载地址,选择download
在这里插入图片描述
然后找一下release版本的.exe文件加载就可以,一路Next就可以安装成功
我们可以来到cmd输入lua,出现以下情况即说明输入成功
在这里插入图片描述

IDE

这里只使用Sublime Text用来学习
中文版下载地址
这里如果中文版地址进不去,也可以下载英文版然后汉化
在这里插入图片描述
我们在桌面新建一个文件夹,新建一个txt,把后缀改成lua
在这里插入图片描述
这里用sublimeText打开
写一个最简单的打印语句,这里不需要分号结尾,按Ctr+B运行

在这里插入图片描述

在线工具

在线工具:lua在线工具
在线体验luatOS->lua测试
在这里插入图片描述

变量

我们可以先在sub里打开文件夹,选择我们要的文件夹
在这里插入图片描述

分类

简单的4种变量类型

  • number
  • string
  • boolean
  • nil

复杂的4种变量类型

  • function
  • table
  • userdata
  • thread

nil

类似于C#中的null

number

lua中所有的数值类型都是number

string

字符串拼接

print(string.format("因为我刚满%d岁", 18))--输出18
  • 1

小写转大写+大写转小写

str="sdASC"
print(string.upper(str))--返回一个新的字符串,而不是修改原有字符串

str"ABC"
print(string.lower(str))--返回一个新的字符串,而不是修改原有字符串
  • 1
  • 2
  • 3
  • 4
  • 5

翻转字符串

str="abc"
print(string.reverse(str))--返回一个新的字符串,而不是修改原有字符串
  • 1
  • 2

字符串索引查找

str="abcdefg"
print(string.find(str,"cde"))
--输出 3	5 说明lua是多返回值的,并且初始索引是1
  • 1
  • 2
  • 3

截取字符串

str="abcdefg"
print(string.sub(str,3,4))
  • 1
  • 2

字符串重复

str="abc"
print(string.rep(str,2))--如果是2就是输出abcabc,是3输出abcabcabc
  • 1
  • 2

字符串修改

str="abc"
print(string.gsub(str,"ab","**"))--输出**c,1 1是指重复一次,如果str等于abcab 那么就会输出2
  • 1
  • 2

字符转ASCII码

print(string.byte("lua",1))--输出108
  • 1

运算符

算数运算符

lua中没有++ – += /=这种运算符
加减乘除都和正常的高级语言相似,这里说一下lua中的幂运算

print("幂运算"..":"..2^10)
  • 1

条件运算符

Lua中的不等于操作是~=
与操作是and
或操作是or
非操作是not

位运算符和三目运算符

lua不支持

条件分支语句

基础语法

a=9
if a>5 then
	a=10
end
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5

加else

a=9
if a<5 then
	a=10
else 
	a=99
end
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

加else if的操作,注意lua中要写成elseif

a=9
if a<5 then
	a=10
elseif a==9 then
	a=99
end
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

lua没有switch

循环

while语句

a=10
while a<15 do
a=a+1
end
print(a) 
  • 1
  • 2
  • 3
  • 4
  • 5

do while语句
和其他语言do while不一样的是lua的函数尾判断的是跳出条件

a=10
repeat 
	a=a-1
until a<5 --在小于5时跳出
print(a)
  • 1
  • 2
  • 3
  • 4
  • 5

for循环

for i=2,5 do--这里其实隐藏了+1的显式声明,所以如果想每次+2就要改成i=2,5,2
	print(i)
end
  • 1
  • 2
  • 3

函数

基本声明

function f1()
	print("调用函数")
end
f1()
  • 1
  • 2
  • 3
  • 4

a = function ()
    print("调用函数")
end
a()
  • 1
  • 2
  • 3
  • 4

然后是对应传入参数的情况

function f1(a )
	print(a)
end
f1(1)--输出1
f1("aaa")--输出aaa
f1()--输出nil
f1(2,3,4)--输出2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

然后是针对于lua的参数返回值,最后输出1

function f1(a)
	return a,"abc",1
end
c=f1(1)
print(c)
  • 1
  • 2
  • 3
  • 4
  • 5

但是如果想把f1的返回值都接住,就要采取如下方式,就可以返回三个了

function f1(a)
	return a,"abc",1
end
c,d,e=f1(1)
print(c,d,e)
  • 1
  • 2
  • 3
  • 4
  • 5

lua不支持函数重载,会调用最后声明的函数

接下来是变长参数
使用变长参数必须要用表把这个存起来

function F7(...)
	arg={...}
	for i=1,#arg
	do
		print(arg[i])
	end
end
F7(1,2,3,4,5,6,7)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最后我们来讲函数嵌套

function F8()
	F9=function()
		print(123)
	end
	return F9
end

f9=F8()
f9()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后看一个比较复杂的案例

function F9(x)
	return function(y)
		return x+y
	end
end
f10=F9(10)
print(f10(5))--输出15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

所有的复杂类型都是表
这里我们先声明一个table,然后打印一下

a={1,2,"asda",true,nil}
for i=1,#a do
	print(a[i])
end
  • 1
  • 2
  • 3
  • 4

发现程序没有打印nil,并且输出大小也只有4
在这里插入图片描述
如果把数组改成这样,这里lua5.15似乎更改了逻辑,在数组中出现nil不会视为中断数组

a={1,2,nil,4,"1231",true,nil}
print(#a)//输出2
b={1,nil,2,4,"1231",true,nil}
print(#b)//输出6
  • 1
  • 2
  • 3
  • 4

二维数组

a={{1,2,3},{4,5,6}}
for i=1,#a do
	for j=1,#a[1] do
		print(a[i][j])
	end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

表支持自定义索引

a={[0]=1,2,3,[-1]=4}
print(a[0])
print(a[-1])
  • 1
  • 2
  • 3
a={[0]=1,2,3,[-1]=4}
print(a[0])
print(a[1])
print(a[2])
print(a[3])
print(a[4])
print(#a)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

输出
在这里插入图片描述

迭代器遍历

主要是用来遍历表
ipairs

a={[0]=1,2,3,4,[-1]=3,[5]=6}
for i,k in ipairs(a) do
	print(i.."_"..k)
end
  • 1
  • 2
  • 3
  • 4

输出
1_2
2_3
3_4
这里可以发现,ipairs遍历从1开始往后遍历,小于等于0的值得不到
并且只能遍历连续索引,断序之后没办法遍历出后面的内容
我们现在的代码只需要把ipairs改成pair
然后输出看看
1_2
2_3
3_4
0_1
-1_3
5_6
可以发现这次把非常规序的在后面输出出来了

基本语法

a={["name"]="DIO",["age"]=14,["1"]=5}
print(a.name) --访问
print(a["age"]) --访问
a.name="dio" --修改
print(a.name)
a["stand"]="TheWorld"--新增
print(a.stand)
--可以用修改的逻辑来达到删除的目的,只是把内容写成nil就可以
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出:
在这里插入图片描述
接下来是遍历字典,要用pairs来遍历

for k,v in pairs(a) do
print(k,v)
end
  • 1
  • 2
  • 3

输出:
在这里插入图片描述

lua中是没有面向对象的概念的,需要自己去实现类
这里说是类其实可以当成一个数组来看,我们写一个类(数组),拥有成员变量和成员函数

Student={
    --年龄
    age=18,
    --性别
    sex=1,
    study=function() --类内声明函数
    print("学习")
        end,
    love=function()
    print("恋爱")
    end
}
Student.speak=function() --类外声明函数
print("说话")
end
Student.speak()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这里有一点要注意

Student={
    --年龄
    age=18,
    study=function()
    print(age)
    end
}
Student.study()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里不会输出18,Student类的age和study函数里打印的age不是一个,正确做法应该是

 print(Student.age)
  • 1

或者是用这种方法

Student={
    --年龄
    age=18,
   learn=function(t)
   print(t.age)
   end
}
Student.learn(Student)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里就要介绍“ . ”和“ ”的区别
使用冒号可以默认把调用者作为参数传入函数中

Student={
    --年龄
    age=18,
   learn=function(t)
   print(t.age)
   end
}
Student:learn()--只是把.改成冒号而已
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

表的公共操作

t1={{age=1,name="123"},{age=2,name="345"}}
t2={name="Theshy",sex=1}

print("插入前的t1长度".." "..#t1)
table.insert(t1, t2)--插入操作
print("插入后的t1长度".." "..#t1)

table.remove(t1)--正常情况下是应该有第二个参数的,如果没有就移除最后被添进来的
print("删除后的t1长度".." "..#t1)

table.table.remove(t1, 2)--移除指定位置

table.sort(t2) --排序

table.sort(t2,function(a,b) --在sort中实现类似lambda表达式的效果实现降序
if a>b then
return true
end
end
)

tb={"123","456","789","10101"}--表的拼接
str=table.concat(tb,",")
print(str)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

多脚本执行相关概念

在Lua中默认都是全局变量,所以需要用local来设置为临时变量
现在的问题是多脚本运行是如何的呢?
首先我们先搞一个测试脚本
这个脚本有如下语句

--TestScript
print("我是测试脚本")
function f1()
print("我是测试函数")
end
  • 1
  • 2
  • 3
  • 4
  • 5

如果想在另一个脚本调用测试脚本的东西应该怎么办呢

--主脚本
require("TestScript")--单引号也行
  • 1
  • 2

这样就会执行测试脚本的功能了
require是有返回值的,前提是其他脚本返回了一个值,这样可以在主脚本使用其他脚本的全局变量
但是如果再执行一次require(“TestScript”)没有效果,因为脚本只会加载一次

package.loaded("脚本名")--可以用来检测是否被加载过
  • 1

所以我们同样可以使用这个语句来实现脚本卸载

package.loaded("脚本名")=nil --再执行require就有效果了
  • 1

大G表

大G表存的是全局变量(不存本地变量)以及其他一些东西

特殊用法

多变量赋值

少了补空

a,b,c=1,2
print(a)--输出1
print(b)--输出2
print(c)--输出nil
  • 1
  • 2
  • 3
  • 4

多了丢弃

a,b,c=1,2,3,4,5
print(a)--输出1
print(b)--输出2
print(c)--输出3
  • 1
  • 2
  • 3
  • 4

多返回值

这里和前面相似,我用一个函数返回多返回值然后用变量去接
少了补空多了丢弃

或和与

and是有假则假,只有遇到nil或false才会认为假

print(1 and 2)
print(0 and 1)
print(nil and 1)
print(false and 2)
print(true and 3)
print(nil and 0)
print(false and nil)
print(nil and false)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述
对于or来说有真则真

print(true or 1)
print(false or 1)
print(nil or 2)
  • 1
  • 2
  • 3

在这里插入图片描述

用and 和 or 实现三目运算符

x=3
y=2
z=x>y and x or y
  • 1
  • 2
  • 3

协程

使用create创建一个协程

f1=function()
print(123)
end
co=coroutine.create(f1)--常用方式
  • 1
  • 2
  • 3
  • 4

协程的本质是一个线程,可以通过type(co)查看
使用wrap创建一个协程,注意,使用wrap创建的协程本质上是函数

f1=function()
print(123)
end
co2=coroutine.wrap(f1)
  • 1
  • 2
  • 3
  • 4

协程的执行

f1=function()
print(123)
end
f2=function()
print(456)
end

co=coroutine.create(f1)
co2=coroutine.wrap(f2)

coroutine.resume(f1)
f2()--由于第二种是函数,直接调用即可
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

协程的挂起

这里由于在死循环中挂起了,现在只调用一次resume所以只会输出一个123

f2=function()
while true do
print(123)
coroutine.yield()--协程的挂起
end
end

co3=coroutine.create(f2)
coroutine.resume(co3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

协程的状态

f1=function()
print(123)
print(coroutine.status(c1))
end

c1=coroutine.create(f1)
print(coroutine.status(c1))
--coroutine.yield(f1)
--print(coroutine.status(c1))--报错
coroutine.resume(c1)
print(coroutine.status(c1))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出
在这里插入图片描述

元表

概念

任何表变量都可以作为另一个表变量的元表
任何表变量都可以有自己的元表
当我们在子表(有元表的表)进行一些特定的操作时
会执行元表中的内容

设置元表

meta={}
myTable={}
--设置元表函数
setmetatable(myTable, meta)--第一个参数子表 第二个参数元表
}
  • 1
  • 2
  • 3
  • 4
  • 5

__tostring

接下来要注意,使用的函数名必须为__tostring,是两个下划线,少一个都不行,起其他名字也不行
当子表要被当作字符串使用时,会默认调用这个元表中的__tostring方法

meta2 = {
    _tostring = function()
        return "啦啦啦"
    end
}
mytable2 = {}
setmetatable(mytable2, meta2)
print(mytable2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

__call

当子表要被当作函数使用时,会默认调用这个元表中的__call方法

meta2 = {
    __call = function()
        return "啦啦啦"
    end
}

mytable2 = {}
setmetatable(mytable2, meta2)
print(mytable2())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出啦啦啦
例2

meta2 = {
    __call = function(self, b)
        print(self.name)  -- 输出表中的name字段
        print(b)
    end
}

mytable2 = { name = "喀喀喀" }
setmetatable(mytable2, meta2)

-- 调用表时只传入一个参数
mytable2(1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出喀喀喀,1

运算符重载

__add

metaAdd={
    __add=function(t1,t2)
    return 5
    end
}

table1={}
table2={}

setmetatable(table1, metaAdd)

print(table1+metaAdd)--输出5
print(table1+table2)--输出5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里只是举add的例子,剩下有很多元表运算符重载可以自行查询
如果要用条件运算符来比较两个对象
这两个对象的元表一定要一致,才能准确调用方法
例如t1和t2分别有不同元表,此时t1==t2可能会产生错误结果,只有t1和t2共享相同元表结果才能准确

__index

__index 当子表中找不到某一个属性时
会到元表中 __index指定的表去找索引

metaIndex = {
 
}
metaIndex.__index={age=2}

table1 = {}
setmetatable(table1, metaIndex)

print(table1.age)  -- 输出 2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

接下来是嵌套
这里table找age找不到,来到元表找,元表也找不到,所以去元表的元表找

metaIndexGrandFather={
    age=2
}
metaIndexGrandFather.__index=metaIndexGrandFather

metaIndexFather={

}
metaIndexFather.__index=metaIndexFather

metaIndextable={}
setmetatable(metaIndexFather, metaIndexGrandFather)
setmetatable(metaIndextable, metaIndexFather)

print(metaIndextable.age)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

__newindex

当赋值时,如果赋值一个不存在的索引,那么会把这个值赋给newindex所指的表中,不会修改自己

meta={}
--meta.__newindex={}
table1={}
setmetatable(table1,meta)
table1.age=1
print(table1.age)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果我保留注释,那么将会输出1,但是如果我把注释取消,那么会输出nil,因为newindex改变了table1寻找age的路径

如果使用rawset,则会忽略__newIndex的设置,只会改自己的变量
同理,rawget会忽略index

面向对象

封装

这里我定义了一个Object类,希望达到高级语言类似实例化的效果,但是Lua中没提供这个,所以需要我们自己实现
所以核心逻辑是声明一个子表,然后把需要实例化的类声明为元表,如果通过子表访问数据没有,通过__index将位置指到元表,就可以正常访问了

Object={}
Object.id=1

function Object:new()
	local obj={}
	self.___index=self
	setmetatable(obj,self)
	return Object
end
local myobj=Object:new()
print(myobj.id)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

继承

我们接着在上面代码的基础上声明一个继承函数
这里使用_G用于在全局声明数组,并且把新创建的表设置成元表

Object={}
Object.id=1

function Object:subClass(className)
	_G[className]={}
	local  obj = _G[className]
	self.__index=self
	setmetatable(obj,self)
	--return obj
end

Object:subClass("LaLaLa")
print(LaLaLa.id)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

多态

这里如果不声明Player的move方法,那么结果就是1,1,如果声明了就什么都不会输出

Object={}
Object.id=1

function Object:subClass(className)
	_G[className]={}
	local obj = _G[className]
	self.__index=self
	setmetatable(obj,self)
	return obj
end

Object:subClass("GameObject")
GameObject.posX=0;
GameObject.posY=0;
function GameObject:Move()
	self.posX=self.posX+1
	self.posY=self.posY+1
	print(self.posX)
	print(self.posY)
end

GameObject:subClass("Player")

function Player:Move()
end

local p1=Player:subClass("Player")
p1:Move()
  • 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

但是现在有一个问题,我确实可以通过多态实现相同函数不同的调用逻辑,但是子类现在要如何调用父类的同名函数?
方法就是在继承时额外加一个base的字段用来保存父类

function Object:subClass(className)
	_G[className]={}
	local obj = _G[className]
	self.__index=self
	setmetatable(obj,self)
	obj.base=self--新加一个Base字段
	return obj
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

不过这时候想要实现就要认真考虑了,不要贸然地使用**:**

接着我们总结一下,因为这部分比较重要

Object = {}

--new 实例化一张空表

function Object:new() 
    local obj = {}
     self.__index = self
    setmetatable(obj, self)
    return obj
end

--继承

function Object:subClass(className)
    _G[className] = {}
    local obj = _G[className]
    obj.base=self
    self.__index = self
    setmetatable(obj, self)
end

Object:subClass("GameObject")

function GameObject:new() 
    local obj = {}
    obj.posX = 0
    obj.posY = 0
    setmetatable(obj, self)
    self.__index = self
    return obj
end

function GameObject:Move()
    self.posX = self.posX + 1
    self.posY = self.posY + 1
   
end

--实例化对象

local obj = GameObject:new()
print(obj.posX)
obj:Move()
print(obj.posX)

local obj2 = GameObject:new()
print(obj2.posX)
obj2:Move()
print(obj2.posX)

--多态
GameObject:subClass("Player") --继承自Gameobject
function Player:Move()
	
    self.base.Move(self) --子类调用父类方法,并且参数使用自己的
end
local obj3=Player:new()
obj3:Move()
  • 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
  • 54
  • 55
  • 56
  • 57
  • 58

自带库

时间

print(os.time())--得到系统时间

local nowTime=os.date("*t")--nowtime是一个表,存了很多时间相关,可以通过pairs遍历查看

print(nowTime.hour)
  • 1
  • 2
  • 3
  • 4
  • 5

数学运算

--绝对值
print(math.abs(-11))

--弧度转角度
print(math.deg(math.pi))
--三角函数转弧度
print(math.cos(math.pi))

--向下向上取整
print(math.floor(2,6))
print(math.ceil(5,2))

--最大最小值
print(math.max(1,2))
print(math.min(1,2))

--小数分离
print(math.modf(1.2)) 
--幂运算 
print(math.pow(2, 5))
--随机数
--先设置随机种子  
math.randomseed(os.time())
print(math.random(100))  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

路径

--用lua脚本加载路径
print(package.path)
  • 1
  • 2

垃圾回收

在lua中,释放内存的方式就是变nil
有自动定时GC的方法
但是尽量不要使用自动垃圾回收

介绍一下collectgarbage,用于手动触发GC

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/502665
推荐阅读
相关标签
  

闽ICP备14008679号