LUA
入门
-
LUA语言不需要分号表示结束(可以有分号也可以没有);当方法体结束时使用end结束;
-
LUA语言中只有nil和false表示为假;其余均为真(包括0);
-
LUA中函数属于一种数据类型,函数参数是可变的、可以有多个返回值;
-
LUA语言中索引值是从1开始而不是0,并且可以断开;
-
LUA中不支持自加,自减操作,如:a++或者a+=1;
-
LUA中使用 .. 进行字符串的连接;
-
LUA使用#可以获取字符串,数组,table表的长度;
-
使用type函数获取给定变量或值的类型;type函数返回值类型是字符串
-
LUA中一切皆数据 ??
-
关键字:
and or not false true
if else elseif while repeat for do then
function in local nil
return break until end
- 注释:
单行注释:--...
多行注释:--[[...--]]
Lua数据类型与变量
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
-
Lua为变量赋值<==>声明一个变量
例:a=1:声明了一个变量a并为其赋值为1;
-
多重赋值:a,b=1,2 <==> a=1 b=2
声明出的变量默认为全局变量;只有在变量前添加local关键字的变量为局部变量;未被赋值的变量默认值为nil;一般开发中轻易不使用全局变量;因为全局变量可以被随意的更改,不安全、不稳定。
-
八种基本类型:
- number 数值型:支持16进制(0X..)表示方法,科学计数法
内置变量: 特点:下划线加大写字母;如:_VERSTON(查看Lua版本)
Lua运算符
- 算数运算符
+:加;-:减;*:乘;/:除;%:取余;^:乘幂;-:负号
- 关系运算符
==:等于;~=:不等于;
>:大于;<:小于;>=:大于等于;<=:小于等于
- 逻辑运算符
and:逻辑与;
or:逻辑或;
and和or可直接返回操作数的值
例:print(nil or 0)---打印值为0;
print(nil and 0)---打印值为nil;
- 其他运算符
..:连接两个字符串
# :一元运算符,用于返回字符串长度
左移:<<;右移:>>
- 运算符优先级
^
not - (取负,一元运算符)
* / %
+ -
..
< > <= >= ~= ==
and
or
Lua循环
LUA中支持循环嵌套
- for循环
语法格式:
for var=exp1,exp2,exp3 do
<执行体>
end
var从exp1变化到exp2,每次变化以exp3为步长进行递增,并执行一次“执行体”。exp3是可选的,如果不指定默认为1。
for的三个表达式在循环开始前一次性求值,以后不会再进行求值
例:
function f(x)
print("function")
return x*2
end
for i=1,f(1) do
print(i)
end
打印结果:function 1 2
循环变量exp1在方法体中不可以被修改,如果进行修改就等同于新建变量
- 泛型for循环
泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。
Lua 编程语言中泛型 for 循环语法格式:
--打印数组a的所有值
a = {"one", "two", "three"}
for i, v in ipairs(a) do --迭代器
print(i, v)
end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组、只能迭代从1开始的数值索引。
结果:1 one 2 two 3 three
- while循环
Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。
语法格式:
while(条件)
do
循环语句
end
- repeat...until循环
repeat...until 循环的条件语句在当前循环结束后判断。条件进行判断前循环体都会执行一次。当条件满足时跳出循环 与do...while(条件)不同:do...while(条件)是当条件为假时跳出循环。
语法格式:
repeat
方法体
until(条件)
LUA中支持循环嵌套
- 循环控制语句
break 语句 退出当前循环或语句,并开始脚本执行紧接着的语句。
goto 语句 将程序的控制点转移到一个标签处。允许将控制流程无条件地转到被标记的语句处。
goto语法格式:
goto Label(标记)
Label的格式:
::Label::
实例:
local a = 1
::s1:: print("执行了goto语句")
a = a + 1
if a<2 then
goto s1
end
输出结果:执行了goto语句
Lua迭代器
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。
在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
- 泛型for迭代器
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
泛型 for 迭代器提供了集合的 key/value 对,
local data={one="aa","bb","cc",[3]="dd",[-1]=123,["two"]=456}
for k,v in ipairs(data) do
print("ipairs():".."K:"..k,"V:"..v)
end
-----------------------
上面代码中,k, v为变量列表、k为索引值;v为索引值所对应的内容值;ipairs(t)为表达式列表、默认迭代函数。
输出结果:
ipairs():K:1 V:bb
ipairs():K:2 V:cc
ipairs():K:3 V:dd
ipairs()函数:连续数值索引的迭代器,只能获取从1开始的数字索引,且索引值必须是连续的!!
语法格式如下:
local data={one="aa","bb","cc",[3]="dd",[-1]=123,["two"]=456}
for k,v in pairs(data) do
print('pairs():'.."K:"..k,"V:"..v)
end
-----------------------
上面代码中,k, v为变量列表、k为索引值;v为索引值所对应的内容值;pairs(t)为表达式列表、迭代函数。
输出结果:
pairs():K:1 V:bb
pairs():K:2 V:cc
pairs():K:one V:aa
pairs():K:3 V:dd
pairs():K:-1 V:123
pairs():K:two V:456
pairs()函数:所有索引值的迭代器,没有连续数值索引的限制。是获取table长度最稳定的方法。
i可以理解为int,去掉int的迭代器,也就去掉的连续数值索引的限定。
泛型 for 的执行过程:
- 首先,初始化,计算 in 后面表达式的值,表达式应该返回泛型 for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
- 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
- 第三,将迭代函数返回的值赋给变量列表。
- 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
- 第五,回到第二步再次调用迭代函数
Lua流程控制
- if语句: if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
语法格式:
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
- if...else语句:if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。
语法格式:
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
-----------------------------------------------
if( 布尔表达式 1)
then
--[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
--[ 在布尔表达式 2 为 true 时执行该语句块 --]
else
--[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end
- if嵌套语句:可以在if 或 else if中使用一个或多个 if 或 else if 语句 。
Lua函数
在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值;是一种数据类型。
需要先定义在调用;因为脚本型语言的执行顺序是从上而下执行
语法格式:
1.
可选的函数范围 function 函数名(参数列表)
函数体
return 函数返回值
end
2.
可选的函数范围 函数名 = function(参数列表)
函数体
return 函数返回值
end
可选的函数范围:制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果需要设置函数为局部函数需要使用关键字 local。
参数列表:多个参数以逗号隔开,函数也可以不带参数。当有参数但未被赋值时,其值为nil。
函数返回值:Lua语言函数可以返回多个值,每个值以逗号隔开。
当函数的多返回值有不需要的时:使用 '_' 接收返回值;
例:
local _,n2=函数多返回值
可变参数: Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。可以使用表table来储存可变参数。
例:
function add(...)
local s = 0
for i, v in ipairs{...} do -->迭代器; {...} 表示一个由所有变长参数构成的数组
s = s + v
end
return s
end
print(add(3,4,5,6,7)) --->25
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …) 或者 select(n, …)
- select('#', …) 返回可变参数的长度;在函数内调用。
- select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。
调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 select 为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。
例:
function f(...)
a = select(3,...) -->从第三个位置开始,变量 a 对应右边变量列表的第一个参数
print (a)
print (select(3,...)) -->打印所有列表参数
end
f(0,1,2,3,4,5)
输出结果为:
2
2 3 4 5
Lua字符串
字符串或串(String)是由数字、字母、下划线组成的一串字符。并且不可以修改。
字符串可以以三种方式来表示:
- 单引号间的一串字符
- 双引号间的一串字符
- [[ ]]间的一串字符:用于直接打印多行;输入的格式是什么样子,输出格式就是什么样子;
支持转义字符
例:
string1='Lua' print("字符串 1 是",string1)
string2="LuaLua" print("字符串 2 是",string2)
string3=[[
多行
Lua
Lua
]] print("字符串 3 是",string3)
输出结果:
字符串 1 是Lua
字符串 2 是LuaLua
字符串 3 是多行
Lua
Lua
相关函数
string.byte():字符转ASCI码
string.char():ASCI码转字符
string.find():字符串查找 参数1:被查找的字符串;参数2:查找的内容;返回值:起始找到的位置下标和结束找到的位置下标
string.reverse():字符串反转
string.lower():字符串转小写
string.upper():字符串转大写
string.len():求字符串的长度
string.sub():字符串截取 方式一:参数:被截取的字符串,起始位置下标(截取到结尾)。
方式二:参数:被截取的字符串,起始位置下标,结束位置下标
sting.format():字符串格式化 参数1:格式字符串、需要被格式化的字符串,其中包含站位符,%d表示数字
参数2:格式化字符串中的内容、即替代占位符的内容
string.rep():字符串重复 参数1:要重复的字符串;参数2:重复的次数
string.gsub():字符串替换 参数1:需要被替换的字符串;参数2:要被替换的字符串子串;参数3:替换的字符串。
两个返回值:返回值1:替换后的字符串;返回值2:被替换的次数。
字符串连接符: ..
tostring() 把一个数字转化成字符串
tonumber() 把一个字符串转化成数字;转化失败后的值为nil
字符串补充
Lua中的字符串与C中的字符数组比较接近;Lua中的第一个字符的索引是1不是0;并且在Lua中的字符串可以放任意不同类型的字符,包括0
s1 = string.char(0X30,0X31,0X32) --0X30 : 16进制数
print(s1) --012
s2 = string.char(0X30,0X00,0X31,0X32) --0X00 : 表示为0
print(s2) --0 12 ‘0与12之间存在一个空格’
Lua数组
数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。
Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
- 一维数组
一维数组是最简单的数组,其逻辑结构是线性表。一维数组可以用for循环出数组中的元素,如下实例:
array = {"Lua", "Tutorial"}
for i= 0, 2 do
print(array[i])
end
----------------------------
local arr={}
arr={'abc',123,[-1]=456,[0]=abc,[1]=def,[4]='1q2w'}
print(arr[1]) --abc
print(arr[-1]) --456
print(arr[4]) --1q2w
输出结果:nil Lua Tutorial
在Lua中索引值是以1为起始,但是可以指定从0开始;也可以用负数作为索引值 没有值则返回为nil
- 多维数组 多维数组即数组中包含数组或一维数组的索引键对应一个数组。 以下是一个三行三列的阵列多维数组:
-- 初始化数组
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
-- 访问数组
for i=1,3 do
for j=1,3 do
print(array[i][j])
end
end
结果:
1 2 3
2 4 6
3 6 9
Lua table(表)
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型。
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
table可以包含各种数据类型。
例:
a={1,"abx",{},function() print(1) end}
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。当以字符来作为索引时可以通过 tabel名.字符索引 来获取值
--数字下标:
a={1,"abx",{},function() print(1) end}
print(a[1]) ==>1
--字符下标 : 无法通过#获取表长
a={
a=1,
b='abx',
c={},
d=function() print(1) end
[",;"]=123
}
print(a["a"]) ==>1
print(a.a) ==>1
print(a[",;"]) ==>123
function a.func()--为表a添加了一个成员函数
print("a.func")
end
a.func() ==>a.func
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
- table表的构造
构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
超出索引后对table(表)进行读取,返回值为nil
注意:
当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度;使用 # 和 table.getn 记录的是从起始索引值为1且索引值连续的元素个数;
可以使用以下方法来代替:
function table_leng(t)
local leng=0
for k, v in pairs(t) do
leng=leng+1
end
return leng;
end
- 全局表 _G
存储Lua中所有的全局变量,包括table(表)类型
使用索引读取元素,例:
_G["table"] =>table: 0x10
_G["table"]["insert"] =>function: 0xc3(table中含有insert(插入)方法)
- self
类似与C#中的this关键字,表示对象本身
local data = {one = "cc", "aa", [4] = 3, ["two"] = "dd"} --相当于一个对象
-- 第一种self调用写法
-- 成员函数定义时,显式加入self变量,对应C#的this关键字
-- 函数内部可以通过self变量获取当前table的其他值或函数
data.func3 = function(self)
print("func3:" .. self.two)
end
-- 调用时,必须使用":",因为":"调用,会对self关键字赋值
data:func3() --func3:dd
------------------------------------------------
-- 第二种self调用写法
-- 隐式给self赋值
function data:func4()
print("func4:" .. self.one)
end
data:func4() --func4:cc
----------------------------
当判断表data中是否存在某一成员函数时使:
表名.方法名;例:a.func
相关函数
- table.concat 把表中所有数据连成一个字符串
- table.insert 向指定位置插入一个数据
- table.move 移动数据
- table.pack 包装成一个表
- table.remove 移除指定位置的数据
- table.sort 排序
- table.unpack 返回一个数组,指定范围的数组
Lua模块与包
模块
-
模块类似于一个封装库,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
-
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
创建自定义模块 module.lua,文件代码格式如下:
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
function module:func4()
print("这是一个成员函数func4!")
end
module:func5 = function(self)
print("这是一个成员函数func5!")
end
return module
require函数
用来加载模块,例:
require("<模块所在文件名>")
或者
require "<模块所在文件名>"
不需要文件后缀:.lua;因为会自动添加
执行 require 后会返回一个由模块常量或函数组成的全局table;如果要返回一个局部table,在模块中需要return局部table
例:
-- 文件名为module.lua,其中有个名为module的全局table
module={}
module.constant="这是一个常量"
-- 文件名为test_module.lua
require("module")
print(module.constant)--"这是一个常量"
--------------------------------
-- 文件名为module.lua,其中有个名为module的局部table
local module={}
module.constant="这是一个常量"
-- 文件名为test_module.lua
require("module")
print(module.constant)--无法正常打印,报错
--更改:
module = require("module")
print(module.constant)--"这是一个常量"
- 每一个模块只能被加载一次,在每次模块加载后package.loaded[加载的模块文件名]会记录已经加载的文件记录,并返回一个Boolean值,为真则证明已经被加载,不能重复加载;所以如果想重复多次加载一个文件,则需要对加载记录进行清除:package.loaded[加载的模块文件名] = nil
- 加载文件的相对路径: "./" 表示当前编写的lua文件,所在的目录 "../" 表示当前编写的lua文件,所在的上级目录
加载机制
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
C包
Lua和C是很容易结合的,使用 C 为 Lua 写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
Lua 元表(Metatable)
在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。
因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。
当 Lua 试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫 __add 的字段,若找到,则调用对应的值。 __add 等即时字段,其对应的值(往往是一个函数或是 table)就是"元方法"。
有两个很重要的函数来处理元表:
-
setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
-
getmetatable(table): 返回对象的元表(metatable)。
对指定的表设置元表:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
例子:
--lua元表 matatable
local t1 = {1,2,3}
local t2 = {'a','b','c'}
meta = {
__tostring = function(t)--元方法,当被扩展的表被以string的方式调用时,调用该方法
local format = '{'
for k,v in pairs(t)
do
format = format .. v .. ","
end
format = format .. "}"
return format
end
}
setmetatable(
t1,--需要进行元表扩展的表
meta
)
setmetatable(
t2,
{
__tostring = function(t)--__tostring元方法,当被扩展的表被以string的方式调用时,调用该方法
local format = '{'
for k,v in pairs(t)
do
format = format .. v .. ","
end
format = format .. "}"
return format
end
})
print(t1)
print(t2)
print('---------------')
mytable = setmetatable(
{key1 = "value1"},
{
__index = function(mytable, key)--__index 元方法;这是 metatable 最常用的键。当你通过键来访问 table 的时候,
如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。
如果__index包含一个表格,Lua会在表格中查找相应的键。
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
打印值:
{1,2,3,}
{a,b,c,}
---------------
value1 metatablevalue