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个步骤:
- 在表中查找,如果找到,返回该元素,找不到则继续
- 判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
- 判断元表有没有__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