LUA

入门

热更新相关

菜鸟教程LUA

LuatOS文档__包含在线编程工具

  • 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;一般开发中轻易不使用全局变量;因为全局变量可以被随意的更改,不安全、不稳定。

  • 八种基本类型:

alt

- 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迭代器

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 元表(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