注释
--这是行注释
--[[
这是块注释
这是块注释
--]]
变量
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,哪怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
--多变量赋值
a,b,c=10,20,"Hello"
a,b=10,20,30--多余的变量会直接忽略
a,b,c=10,20--没有赋值的变量会直接赋值默认值
数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
nil
lua与C#不同,在C#中使用变量前要先定义,lua中可以直接使用不管有没有定义,如果没有定义的话就默认是nil值,如果定义了就是所定义的那个值
对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉
Lua 默认只有一种 number 类型 – double(双精度)类型
boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true
string
字符串由一对双引号或单引号来表示。
在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字
print("2"+"6");--字符串算数相加(必须保证字符串里是数字类型)
print("2"+6);
print("2".."6");--字符串连接(组拼)
使用 # 来计算字符串的长度,放在字符串前面
table
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始。
对table的各种操作
--向表中添加值
tab1.key1="123";
tab1["key2"]="123";
tab1[10]=100;
--修改数据
tab1.key2="456";
--删除数据
tab1.key1=nil;
--删除表
tab1=nil;
function
在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里
- 可以作为数据赋值
- 可以作为参数传递
- 可以返回多个值
--定义
function fact(n)
if n==1 then
return n
else
return n*fact(n-1);
end
end
--使用
print(fact(3));
fact2=fact--将fact的功能赋值给fact2
print(fact2(3))
--函数作为参数传递
function test(tab1,fun)
for k,v in pairs(tab1) do
fun(k,v)
end
end
可以以匿名函数(anonymous function)的方式通过参数传递
匿名函数直接在引用时定义
testFun(tab,
function(key,val)--匿名函数
return key.."="..val;
end
)
可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 … 表示函数有可变的参数。
可以通过 select(“#”,…) 来获取可变参数的数量
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. select("#",...) .. " 个数")
return result/select("#",...)
end
print("平均值为",average(10,5,3,4,5,6))
thread
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
循环
while循环
while(condition) do
statements
end
a=1
while(a<=20) do
print(a)
a=a+1;
end
for循环
1.数值for循环
for var=start,end,step do
循环体
end
这里var会从start变化到end,每次变化一step执行
for a=2,10,2 do
print(a)
end
2.泛型for循环
tab1={key="value1",key2="value2"}
for k,v in pairs(tab1) do
print(k,v)
end
repeat until
重复执行循环,直到 指定的条件为真时为止
repeat
循环体
until(condition)
repeat
print(a)
a=a+1;
until(a>10)--满足条件后就不再执行
Lua 流程控制
Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其他指定代码。
if(0)
then
print("0 为 true")
end
a=60
if(a<=50) then
print("a<=50")
elseif(a<=100) then
print("a<=100")
else
print("都不是")
end
Lua中的数学运算符
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:
-
算术运算符
-
关系运算符
-
逻辑运算符
-
其他运算符
Lua 字符串
字符串或串(String)是由数字、字母、下划线组成的一串字符。
在 Lua 中,字符串是一种基本的数据类型,用于存储文本数据。
Lua 中的字符串可以包含任意字符,包括字母、数字、符号、空格以及其他特殊字符。
Lua 语言中字符串可以使用以下三种方式来表示:
双引号""
单引号''
双中括号[[]]
string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'runoob.com'
print("字符串 2 是",string2)
string3 = [["Lua 教程"]]
print("字符串 3 是",string3)
转义字符
字符串操作
Lua 提供了很多的方法来支持字符串的操作:
-
string.upper(argument):
字符串全部转为大写字母。 -
string.lower(argument):
字符串全部转为小写字母。 -
string.gsub(mainString,findString,replaceString,num)
在字符串中替换。
mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换) -
string.find (str, substr, [init, [plain]])
在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。
init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。
plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用使用正则模式匹配。 -
string.reverse(arg)
字符串反转 -
string.format(…)
返回一个类似printf的格式化字符串 -
string.char(arg) 和 string.byte(arg[,int])
char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。 -
string.len(arg)
计算字符串长度。 -
string.rep(string, n)
返回字符串string的n个拷贝 -
…
链接两个字符串 -
string.gmatch(str, pattern)
返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。 -
string.match(str, pattern, init)
string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
str="ShaBi"
str1=string.upper(str)
str2=string.lower(str)
str4=string.gsub(str,"i",124,2)
index=string.find(str,"a",1)
str5=string.reverse(str)
str6=string.format("加法运算:%d+%d=%d",num1,num2,(num1+num2))
s1=string.char(97,98,99,100)
s2=string.byte("ABCD",4)
s3=string.byte("ABCD")
length=string.len("abc")
str7=string.rep("abcd",2)
for word in string.gmatch("Hello Lua user", "%a+")
do print(word)
end
str8= string.match("I have 2 questions for you.", "%d+ %a+")
数组
我们可以使用整数索引来访问数组元素,如果指定的索引没有值则返回 nil。
除此外我们还可以以负数为数组索引值:
array = {}
for i= -2, 2 do
array[i] = i *2
end
for i = -2,2 do
print(array[i])
end
多维数组
多维数组即数组中包含数组或一维数组的索引键对应一个数组。
--多维数组
array={}
for i=1,3 do
array[i]={}
for j=1,3 do
array[i][j]=i*j;
end
end
迭代器
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。
在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
调用迭代函数时会将状态变量和控制变量当做参数传递给迭代函数,状态变量只会在第一次调用的时候赋值
array={"Lua","C#","Java"}
for k,v in pairs(array) do
print(k,v)
end
for k,v in ipairs(array) do
print(k,v)
end
--pairs迭代table,遍历表中所有的key跟value
--ipairs按照索引从1开始,递增遍历,遇到nil就停止
泛型 for 的执行过程:
首先,初始化,计算 in 后面表达式的值,表达式应该返回泛型 for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为nil循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数
表
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
-
able.concat (table [, sep [, start [, end]]]):
concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。 -
table.insert (table, [pos,] value):
在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. -
table.maxn (table)
指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现) -
table.remove (table [, pos])
返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 -
table.sort (table [, comp])
对给定的table进行升序排序。
mytable={"H","E","L","L","O"}
table.concat(mytable)
table.concat(mytable," ")--拼接时使用空格隔开
table.concat(mytable,2,4)--拼接时开始和结束索引
--都会保证索引的连续性
table.insert (mytable,2,"A")
table.remove(mytable,2)
table.sort(mytable)
Lua 模块与包
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
和C#中的类相似,但是不需要声明对象
C 包
Lua和C是很容易结合的,使用 C 为 Lua 写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。
元表(Metatable)
这块有点没听懂
在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。
因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。
原表对普通表进行行为拓展
-
setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
-
getmetatable(table): 返回对象的元表(metatable)。
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
metatable
如果元表有键则getmetatable(table)会返回键而不是原表,所以使用__metatable可以保护元素,禁止用户访问元素中的成员或者修改元素
__index 元方法
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
mymetatable={
__index = function(mytable, key)
if(key>=10) then
return "Lua"
end
end
}
__newindex 元方法
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
为表添加操作符
__add 键包含在元表中,并进行相加操作
mytable={"Lua","ex","hao","6"}
mymetatable={
__add=function(tab,newtab)
local mi=0;
for k,v in pairs(tab)do
if(k>mi) then
mi=key
end
end
for k,v in pairs(newtab) do
mi=mi+1;
table.insert(tab,mi,v)
end
return tab;
end
}
__call 元方法
元表定义了_call之后,将表当作函数来使用时会调用该方法
__call 元方法在 Lua 调用一个值时调用。
__call=function(tab,arg)
print(arg);
return "ff"
end
v=mytable(3)
print(v)
__tostring 元方法
__tostring 元方法用于修改表的输出行为。
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
Lua 协同程序(coroutine)
Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
协同程序可以理解为一种特殊的线程,可以暂停和恢复其执行,从而允许非抢占式的多任务处理。
协同是非常强大的功能,但是用起来也很复杂。
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
协同函数有多个返回值
--定义协同函数
co=coroutine.create(
function add(a,b)
print(a,b)
print(coroutine.running())--返回正在跑的 coroutine
coroutine.yield(a*b,a/b)--暂停协同函数
return a%b,a/b+1
end
)
res1.res2,res3=coroutine.resume(co,10,40)--接受yield的返回值
res1.res2,res3=coroutine.resume(co)--接受函数执行到最后的返回值
print(coroutine.status(co))--查看 coroutine 的状态
--启动/继续运行协同函数
coroutine.resume(co,20,30)
--继续运行时不用传递参数
coroutine.resume(co)
Lua中简单模式下文件的读取
Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。
简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
能被记事本打开的都是文本文件,除此之外都是二进制文件
file=io.open("data1.text","r")
io.input(file)
print(io.read())
io.close(file)
file=io.open("data1.text","w")
file=io.open("data1.text","a")
io.output(file)
io.write("99")
io.close(file)
--从当前位置读取整个文件
print(io.read(*a"))
--读取一个数字并返回它
print(io.read(*n"))
--读取下一行
print(io.read(*l"))
--返回一个指定字符个数的字符串
print(io.read((10))
完全模式(complete model) 使用外部的文件句柄来实现。它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法
-- 以只读方式打开文件
file = io.open("data1.text", "r")
-- 输出文件第一行
print(file:read())
-- 关闭打开的文件
file:close()
-- 以附加的方式打开只写文件
file = io.open("data1.text", "a")
-- 在文件最后一行添加 Lua 注释
file:write("--test")
-- 关闭打开的文件
file:close()
错误处理
程序运行中错误处理是必要的,在我们进行文件操作,数据转移及web service 调用过程中都会出现不可预期的错误。如果不注重错误信息的处理,就会造成信息泄露,程序无法运行等情况。
任何程序语言中,都需要错误处理。错误类型有:
- 语法错误
语法错误通常是由于对程序的组件(如运算符、表达式)使用不当引起的
lua: test.lua:2: syntax error near ‘==’
- 运行错误
运行错误是程序可以正常执行,但是会输出报错信息。
Lua 垃圾回收
Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua中的面向对象怎么实现
在Lua中实现简单的面向对象
我们知道,对象由属性和方法组成。LUA中最基本的结构是table,所以需要用table来描述对象的属性。
lua 中的 function 可以用来表示方法。那么LUA中的类可以通过 table + function 模拟出来。
实现面向对象的三种方法
--1
person={name="ZnCu",age=99}
person.eat=function()
print(person.name.."在吃饭")
end
person.eat()
--2
function person.eat()
print(person.name.."在吃饭")
end
person.eat()
--3
person={name="ZnCu",age=99,eat=function()
print(person.name.."在吃饭")
end}
person.eat()
通过冒号和点来定义调用函数的使用区别
使用冒号时系统会自动传递当前的table给self 例如该函数self=person
当通过.来调用的时候,self不会自动赋值,我们必须通过第一个参数来传递当前table
person={name="ZnCu",age=99}
person.eat=function(self)
print(self.name.."在吃饭")
end
function person:eat()
print(self.name.."")
end
a=person
person:eat();
person.eat(a)
--使用冒号时系统会自动传递当前的table给self 例如该函数self=person
--当通过.来调用的时候,self不会自动赋值,我们必须通过第一个参数来传递当前table
创建构造函数,以用于构造拥有相同属性和函数的对象
person={name="ZnCu",age=99}
person.eat=function(self)
print(self.name.."在吃饭")
end
function person:new()
local t={}
setmetatable(t,{__index=self})
return t
end
person1=person:new()
person1:eat()
Lua中面向对象实现的注意事项
--可以直接传入现成的表作为参数,通过这种方法构建的对象会拥有参数表和person的特性
function person:new(o)
local t=o or {}
setmetatable(t,{__index=self})
return t
end
Lua中的继承如何实现
继承是指一个对象直接使用另一对象的属性和方法。可用于扩展基础类的属性和方法。
Student=Person:new()--Student继承Person类
Student.grade=1
stu1=Student:new()
stu1:eat()