首页 > 其他分享 >Lua学习笔记

Lua学习笔记

时间:2022-11-29 21:41:29浏览次数:50  
标签:__ end coroutine -- mytable 笔记 学习 Lua print


1.注释

--单行注释

 

多行注释

--[[

--]]

 

2.变量命名

最好不要使用下划线加大写字母作为标示符,因为lua内部的保留字也是这样命名的。Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。

 

3.全局变量

默认情况下,变量总是全局的。局部变量使用local

变量不需要声明,在赋值时会创建。

访问一个没有初始化的全局变量也不会报错,不过会返回nil。

如果想删除一个全局变量,把这个变量设置为nil

 

4.数据类型

boolean

number 双精度的浮点类型

string

function

userdata 表示存储在变量中的C数据结构

table 一种数据结构,用来帮助我们创建不同的数据类型,如数组,字典等

thread 用于执行协同程序

 

4.1 运算符

~= 不等于

and 逻辑与操作符

or 逻辑或操作符

not 逻辑非操作符

.. 连接两个字符串

# 计算字符串所占的字节数,一个中文字符,占据两个字节。

 

4.2 userdata 自定义类型

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

4.3 closure 闭包

一个函数定义在另一个函数中,位于内部的函数可以访问外部函数中的所有局部变量,这种特征就是词法作用域。而这些局部变量则称为该函数的upvalue。

局部变量在函数返回后继续存在,并且返回的函数可以正常的调用这个局部变量,独立执行其逻辑的现象,在Lua中称为闭包

 

5.string类型

  • string可以有一对双引号或者单引号来表示。也可以用2个括号“[[]]”来表示一块字符串。
  • 连接两个字符串使用的是两个点号“..”
  • 如果使用“+”号,会尝试把数字字符串转换成数字,如果无法转换会报错。
  • 使用#号,计算字符串所占的字节数,一个中文字符,占据两个字节。

 

6.table

不同于其他语言的数组把0作为数组的初始索引,在lua中表的默认初始索引从1开始。

如果我们为table a设置元素,然后将a赋值给b,则a和b都指向同一个内存。如果a设置为nil,b还是可以访问table元素。

-- 简单的 table
mytable = {}
print("mytable 的类型是 ",type(mytable))

mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1 的元素是 ", mytable[1])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

-- alternatetable和mytable的是指同一个 table
alternatetable = mytable

print("alternatetable 索引为 1 的元素是 ", alternatetable[1])
print("alternatetable 索引为 wow 的元素是 ", alternatetable["wow"])

alternatetable["wow"] = "修改后"

print("alternatetable 索引为 wow 的元素是 ", alternatetable["wow"])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

-- 释放变量
alternatetable = nil
print("alternatetable 是 ", alternatetable)

-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

mytable = nil
print("mytable 是 ", mytable)

执行结果

mytable 的类型是  table
mytable 索引为 1 的元素是 Lua
mytable 索引为 wow 的元素是 修改前
alternatetable 索引为 1 的元素是 Lua
alternatetable 索引为 wow 的元素是 修改前
alternatetable 索引为 wow 的元素是 修改后
mytable 索引为 wow 的元素是 修改后
alternatetable 是 nil
mytable 索引为 wow 的元素是 修改后
mytable 是 nil

浅拷贝

在lua中,使用“=”进行浅拷贝,根据拷贝对象的不同,又分为两种情况:

1.拷贝对象为string、number、boolean等基础类型时,拷贝的过程是进行复制粘贴。创建一个新的对象,拷贝出来的新对象和原来的对象互不影响,互不干涉,所以修改拷贝出来的对象的值不会影响到原来的对象的值!

local value_A=10
local value_B=value_A
print("A的值:"..value_A)
print("B的值:"..value_B)
value_B=5
print("A的值:"..value_A)
print("B的值:"..value_B)

输出结果

A的值:10
B的值:10
A的值:10
B的值:5

2.拷贝对象的类型为table,拷贝出来的对象和拷贝前的实际上是同一个对象,占同一个内存,简单点说就是一个人的两个名字。如果改变了拷贝出来对象的值,原对象也会随之改变。

local tab={}
tab["aaa"]="自强"

for k,v in pairs(tab) do
print(k.."对应的账号:"..v)
end

local temp=tab
temp["aaa"]="爱上游戏开发"

for k,v in pairs(tab) do
print("拷贝前的对象:"..k.."对应的账号:"..v)
end
for k,v in pairs(temp) do
print("拷贝后的对象:"..k.."对应的账号:"..v)
end

输出结果

aaa对应的账号:自强
拷贝前的对象:aaa对应的:爱上游戏开发
拷贝后的对象:aaa对应的:爱上游戏开发

深拷贝

如果想修改拷贝后的对象的值,使原对象不受影响,那么就要采用深拷贝了。

Lua中没有提供这样的api的,那就自己封装一个函数,递归拷贝table中所有元素,如果有元表则设置元表即可!

function TableDeepCopy(targetTab)
if targetTab == nil then
return nil
end
if type(targetTab) ~= "table" then
return targetTab
end
local new_tab = {}
local mt = getmetatable(targetTab)
if mt ~= nil then
setmetatable(new_tab, mt)
end
for i, v in pairs(targetTab) do
if type(v) == "table" then
new_tab[i] = TableDeepCopy(v)
else
new_tab[i] = v
end
end
return new_tab
end

local tab = {}
tab.abc = 123
local ss = TableDeepCopy(tab)
ss.abc = 111
print(tab.abc)
print(ss.abc)

输出结果

123
111

 

7.function

函数以function开头,以end结尾,可以有返回值,也可以没有,传入参数类型任意

可以把函数作为参数

还可以使用匿名函数作为参数

myprint = function(param)
print("result ",param)
end

function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
return 1,2,3
end
myprint(10)
-- myprint 函数作为参数传递
local a, b, c = add(2,5,myprint)
print(a, b, c)

可变参数

function average(...)
result = 0
local arg={...} --> arg 为一个表,局部变量
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. #arg .. " 个数")
return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

有时候我们可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:

我们也可以通过 select("#",...) 来获取可变参数的数量:

通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …)或者 select(n, …)

do  
function foo(...)
for i = 1, select('#', ...) do -->获取参数总数
local arg = select(i, ...); -->读取参数
print("arg", arg);
end
end

foo(1, 2, 3, 4);
end

8.thread

在lua里,最主要的线程是协同程序。跟线程差不多,拥有自己独立的栈,局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。

线程和协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起才会被暂停。

 

coroutine原理

在底层实现就是一个线程。当create一个coroutine的时候就是在新线程中注册了一个事件。

当使用resume触发事件的时候,coroutine的create函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。

1)使用coroutine.create方法

创建协同程序,参数是一个函数,返回类型为coroutine,使用coroutine.resume启动协同程序

co = coroutine.create(

function(a, b)

print(a+b)

end

)

coroutine.resume(co, 10.5, 390)

2)使用coroutine.wrap方法

创建协同程序,参数是一个函数,返回类型为函数。直接调用返回的函数,就启动协同程序

co = coroutine.wrap(

function(a, b)

print(a+b)

end

)

co(2.5, 7.5)

3)coroutine.status()

查看协程的状态,coroutine的状态有三种:

dead,当协程程序都执行完成后,coroutine状态为dead,同时不能被resume重启

suspend(暂停,挂起),执行coroutine.yield,挂起coroutine

running,运行状态

4)coroutine.yield()

挂起coroutine,使用coroutine.resume(co)再次从挂起位置继续运行

co = coroutine.create(

function(a, b)

print(a+b)

coroutine.yield()

print(a-b)

end

)

coroutine.resume(co, 10.5, 390)

print("Hello")

coroutine.resume(co)

可以填入参数,coroutine.resume会返回yield填入的参数

有两种返回yield的方式,

一种是在协程程序中,调用coroutine.yield

另一种是在协程程序之外的函数中,调用return coroutine.yield

function f (a)
return coroutine.yield(2*a)
end

co = coroutine.create(
function(a, b)
print(f(a))
print(a+b)
coroutine.yield(a,b)
print(a-b)
return a*b,a
end
)

res1, res2, res3 = coroutine.resume(co, 10.5, 390)
print(res1,res2,res3)
print("Hello")
res1, res2, res3 = coroutine.resume(co)
print(res1,res2,res3)

5)coroutine.resume()

重新启动coroutine

co = coroutine.create(

function(a, b)

return a*b

end

)

res1, res2 = coroutine.resume(co, 10, 390)
print(res1, res2)

默认返回一个执行是否成功的状态(boolean),如果创建的协程程序有返回值,会在后面返回。

 

9.while语句

a = 1

while (a<20) do

print(a)

a = a + 1 --lua没有自增

end

 

10.for语句

var会从start变化到end,每次变化step,step默认为1

for var=start,end,step do

循环体

end

例子:如果倒序,需要把step设置为负数

for i=10,0,-1 do

print(i)

end

例子:输出0,1,2,3,4,5,6,7,8,9,10,最后10也会输出,此处要注意

for i = 0, 10 do

print(i)

end

 

11.if语句

if n>1 then

print("n>1")

elseif n==0 then

print("n==0")

end

 

12.模块

local m = require("module"),调用一个封装的模块

 

13.元表

lua提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。

有两个很重要的函数来处理元表:

setmetatable(table, metatable) : 对指定table设置元表

getmetatable(table) : 返回对象的元表

 

__index元方法:

当通过键访问table时,如果这个键没有值,那么lua就会寻找table的metatable的__index元方法。

如果__index包含一个函数,lua就会调用那个函数,table和键会作为参数传递给函数。

__index = function(tab, key)

如果__index包含一个表,lua会在表中查找相应的键。如果不存在,返回nil,如果存在则由__index返回结果。

 

小结:

Lua查找一个表元素时的规则,其实就是如下3个步骤:

  1. 在表中查找,如果找到,返回该元素,找不到则继续
  2. 判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
  3. 判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

 

__newindex元方法:

__newindex元方法用来对表更新(给表增加新的键值对),__index元方法用来对表访问。

__newindex元方法,可以包含一个函数或一个表

__newindex = function(tab,key,value)

 

1.当给表的一个缺少索引赋值,

  • 判断是否有元表,如果没有元表,创建索引,进行赋值。
  • 如果有元表,解释器就会查找table的metatable的__newindex元方法,如果__newindex方法是一个函数,则返回该函数的返回值,但不进行赋值操作,如果需要赋值,需要调用rawset函数。如果__newindex方法是一个表,则把新添加的值,添加到__newindex方法对应的表中。如果__newindex不存在,创建索引,进行赋值。

2.当表中已经存在索引键,则会进行赋值,而不调用元方法__newindex。

 

注意:在调用__newindex元方法后,并没有进行赋值操作,需要调用 rawset 函数来更新表。rawset(table, key, value)

 

__call元方法:

在元表中存在__call元方法时,可以把表当做函数使用

__tostring 元方法:

用于修改表的输出行为。

 

__add元方法:

对应“+”运算符,遍历新添加的表,依次插入到原来表的后面

--取得表的元素个数,实质取得最后的索引
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end

mytable={"Lua","Java"}
newtable={"Php","C#"}
mymetatable={
__add = function(tab, newtable)

--取得原来表的个数,因为lua中表的下标是从1开始的
--(原来表的个数 + 1)可以作为新表插入的位置索引
local index = table_maxn(tab)

for k,v in pairs(newtable) do
index = index + 1
table.insert(tab, index, v)
end

return tab

end,

__call = function(tab, arg1,arg2, arg3)
print(arg1, arg2, arg3)
end

}

mytable=setmetatable(mytable, mymetatable)
mytable=mytable + newtable

for k,v in pairs(mytable) do
print(k,v)
end

mytable(324, 123, 22)

 

14.IO文件操作

 

简单模式:简单模式使用一个当前输入文件或一个当前输出文件。

file = io.open("data.txt", "r")

io.input(file)

print(io.read("*a"))--从当前位置,读取整个文件

io.close(file)

 

完全模式:通常我们需要在同一时间处理多个文件,需要使用完全模式。

file = io.open("data.txt", "r")

print(file:read("*a"))

file:close()

 

15.垃圾回收机制

Lua 运行了一个垃圾收集器来收集所有不用的对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。

Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。

垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。

垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。

collectgarbage("collect"): 做一次完整的垃圾收集循环。

collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。

 

16.面向对象的实现(重要)

我们知道,对象由属性和方法组成。LUA中最基本的结构是table,所以需要用table来描述对象的属性。

lua中的function可以用来表示方法。至于继承,可以通过metetable模拟出来。

 

1.简单的面向对象实现,但还不能实例化,创建多个不同的对象

--对象包括属性和方法

person={ name="ss", age = 23}



--通过冒号定义方法,可以在方法内部使用self,这里的self代表person

function person:walk()

print(self.name.."在行走")

end

--当通过冒号调用时,系统会自动给self赋值;当通过点号调用时,必须传入self

person:walk()

--person.walk(person)



a = person

person = nil

--当person被置空,通过a还可以调用方法walk

a:walk()

 

2.完整的面向对象实现:

person={ name="ss", age = 23}



function person:walk()

print(self.name.."在行走")

end

--可以传入一个表作为参数,也可以不传入参数

--设置tmp为local局部变量,防止外部调用

--调用一个属性,如果tmp中不存在,那么在元表的__index元方法对应的表person中查找

function person:new(o)

local tmp = o or {}

setmetatable(tmp, { __index=self })

--跟上面的写法,作用一样

--setmetatable(tmp, self)

--self.__index = self

return tmp

end



--没有传入参数

--调用person:new()方法,创建一个空表,然后把person表设置为元表,并赋值给__index元方法

person1 = person:new()

--从元表中找到name属性和walk方法

person1.name = "aa"

person1:walk()



--传入一个表作为参数

person2 = person:new({height=100})

--从传入的表中找到属性height

print(person2.height)



--继承实现,student的元表是person,stu1的元表是student

student = person:new()

student.name = "xiaoming"

student.grade = 5



stu1 = student:new()

print(stu1.name.."的班级是"..stu1.grade)

stu1:walk()

 

输出:

aa在行走

100

xiaoming的班级是5

xiaoming在行走

 

 

 

标签:__,end,coroutine,--,mytable,笔记,学习,Lua,print
From: https://blog.51cto.com/u_6871414/5897034

相关文章

  • 【最详细易懂】C++和Lua交互总结
    一、Lua与C++的交互机制——Lua堆栈Lua和C++的交互机制的基础在于Lua提供了一个虚拟栈,C++和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C++想从Lua中调用一个......
  • k8s学习笔记
    1.pv学习mysql-pv.yamlapiVersion:v1kind:PersistentVolume#申明资源是pvmetadata:name:pv-mysql-datadir#pv名称labels:pv:mysql-datadir#pv标签,pvc关......
  • C#设计模式读书笔记之设计模式的设计原则
    设计模式的设计原则:(重要性从上往下排列)开闭原则:对扩展开放,对修改关闭依赖倒转原则:高层模块不应该依赖底层模块,它们都应该依赖抽象;要针对抽象层编程,而不要针对具体类编程。......
  • lua获取数组的长度
    获取数组的长度对于一个数组我们通常可以使用#来获取其长度tabletest={1,2,3,5,7}print(#tabletest)------》5使用这两种方法都能得到这个数组的长度,但是如果tabletest=......
  • java学习问题
    1、nacosConnectionrefused:connect由于配置文件配置错误引起的。我的nacos是部署在另一台linux服务器的,yml具体配置如下: ......
  • 注册不到两年半Github标星39k+,吴恩达、李航老师的作品的笔记和代码实现
    2017年11月,我注册了github,现在差不多两年半了,一共收获了约39000star,排名个人用户81。今天,我就对我的github做下介绍,里面的几个仓库,非常适合机器学习和深度学习入门。......
  • 首发:徐亦达教授团队最新发表的两篇机器学习论文
    徐亦达团队在AsianConferenceonMachineLearning的发表了两篇机器学习论文,本人得到徐老师授权在本站发布论文。论文1:RealisticImageGenerationusingRegion-phrase......
  • angr_ctf——从0学习angr(二):状态操作和约束求解
    状态操作angr中提到的状态(state)实际上是一个Simstate类,该类可由Project预设得到。预设完成后,还可以根据需要对某些部分进行细化操作。一个state包含了程序运行到某个阶段......
  • AES算法学习02:原理总结和实现(ECB)
    一原理介绍:其实AES就是对16byte(128bit)数据进行加密的过程。说白了就是把128位通过一系列的变化变成另一个128数据。   这里主要用到2个关键的东西。密钥(key)这个是绝......
  • Android约束布局:ConstraintLayout学习文章记录
    (一)Android新特性介绍,ConstraintLayout完全解析(二)ConstraintLayout完全解析快来优化你的布局吧参考官方文档:​​https://developer.android.com/reference/android/suppor......