一, 概述
- lua作为一种脚本语言,安装轻便,常用于嵌入应用程序中,为应用程序提供灵活的扩展和定制的功能。底层是使用c语言进行开发的
二,安装
- 下载安装包:http://www.lua.org/ftp/lua-5.3.0.tar.gz
- copy安装包到Linux系统,tar zxvf lua-5.3.0.tar.gz
- make linux test
- 如果对客户端的安装目录有要求的话,可以修改makefile中的install_top为自己想要安装的目录(这里最好是修改成绝对目录,因为如果是相对目录的话,cd之后相对目录就会有问题)
- make install,执行makefile
- 安装完成,客户端地址:install_top/usr/local/bin/lua
可能发生的报错:参考
三,执行方式
- 在客户端直接执行
./lua
- 编写脚本,执行lua 脚本
lua 脚本名称
- 编写lua脚本,脚本中第一行标明 “#!install_top/usr/local/bin//lua”;修改脚本为可执行态,chmod+x 脚本;./脚本 即可执行
- vi test.lua
test.lua 内容:
#!./lua
print(“hello ,my first lua print”)- 修改脚本为可执行态
chmod +x test.lua- 执行脚本
./test.lua
hello ,my first lua print
四, 基本语法
-
支持的数据类型:
- nil:空数据类型;变量无效值;变量默认值 - number:双精度类型的小数,在lua中只有这一种小数类型 - string:字符串类型 - boolean:布尔类型(true/false) - function:函数 - table:字典和list都用table来表示
-
变量
1)变量声明
a=123 -- 全局变量:默认的变量的声明是全局变量,在整个文件范围内生效
do
local a=123 -- 局部变量,使用local关键字声明的变量是局部变量,只在声明局部变量的语句块(end关键字的出现标识一个语句块的结束)内生效
print(a) -- 123
end
print(a) -- nil
-- 表中的域:在table中声明的变量,那么只在table中生效
2)变量赋值
variable=123 -- 单个变量赋值
-- 多个变量赋值
a,b,c=1,2,3,这种多个变量同时进行赋值的方式,如果变量的个数大于值的个数,那么从左往右,多余的变量会被赋值成nil。如果是变量的个数小于值的个数,那么多余的值会被忽略掉
a,b,c=1,2
print(a,b,c) -- 1 2 nil
a,b=4,5,6
print(a,b,c) -- 4 5 nil
- 流程控制
-- 循环语句:while(true) do .... end
i=0
while(i<3)
do
print(i) -- 0 1 2
i=i+1
end
-- for语句数值循环:for var=expr1,expr2,expr3 do ... end ==>(var:初始变量名,expr1:变量初始值,expr2:变量迭代结束值,expr3:变量迭代表达式【可选,默认是+1】)
for i=1,7,i+2 do
print(i) -- 1 6
end
-- for语句泛型循环:for k,v in ipairs(xxx) do ... end ==>(ipairs只会遍历key是数值的元素);for k,v in pairs(xxx) do ... end ==>(pairs会遍历所有的key)
tb={'a', 'b', 'c'}
tb['k']='d'
tb[5]='hh'
for k,v in ipairs(tb) do
print(k,v)
end
output:
1 a
2 b
3 c
for k,v in pairs(tb) do
print(k,v)
end
ouput:
1 a
2 b
3 c
k d
5 hh
-- repeat ... until(condition)
i=0
repeat
print('----------',i)
i=i+1
until(i==3)
ouput:
---------- 0
---------- 1
---------- 2
-- break关键字
i=0
repeat
print('----------',i)
i=i+1
if (i>1) then break end
until(i==3)
ouput:
---------- 0
---------- 1
-- goto关键字
-- 声明一个label
:: mylabel :: print('this is a label')
print('==================')
-- 跳到指定的label处
goto mylabel
-- 注意:上面的代码会一直循环的print,所以在使用goto的时候需要时在指定的条件下才可以执行
i=0
:: mylabel :: do
print('this is a label')
end
i=i+1
if(i<3) then
goto mylabel
end
output:
hello ,my first lua print
this is a label
this is a label
this is a label
-- if判断语句
if() then ''''
else ''''
end
- 函数
函数的定义:
[local] function myFun(variables)
xxx
return
end
function: 定义函数的关键字
local:标识定义的函数是局部函数,只在当前局部生效。默认情况下不加local标识是全局函数,全局都有效使用
myFun:自定义的函数名称
variables:函数所需的形参,可以传n>=0个参数。这里的参数可以是任意基本类型,或者是函数
return:如果函数有返回值,用return来进行标识
end:函数定义的结束
函数的用法:
1. 根据函数的使用范围通过myFun(vs)来进行调用
2. 形参和实参:形参指的是我们在定义函数的时候指定参数列表;实参指的是我们在实际执行函数的时候所传的参数;
在lua中,形参和实参的个数是可以不一致的,多者忽略,少者补nil的原则
----------------------------------------
-- 函数定义
local function my(a,b)
print(a,b)
end
----------------------------------------
-- 形参多于实参的例子
my(1)
output:
1 nil
-- 形参少于实参的例子
my(1,2,3)
output:
1 2
----------------------------------------
-- 把函数当做参数传递
local function f1(a)
print('this is f1')
print(a)
end
local function f2(f)
f(111)
print('this is f2')
end
-- 把函数f2当做参数传给f1
f2(f1)
output:
this is f1
111
this is f2
----------------------------------------
-- return 可以有多个值,接收变量也秉持“多者忽略,少者补nil”的原则
function f()
return 1,2
end
-- 返回接收变量多于返回值
a,b,c=f()
print(a,b,c)
output:
1 2 nil
-- 返回接收变量少于返回值
a=f()
print(a)
output:
1
----------------------------------------
-- 可变参数:...来表示可变参数
function f(...)
-- 把可变参数整体赋值给一个变量,vs为一个table,key是从1开始的自增,value为传递的参数
vs = {...}
-- select("#",...)可以用来获取可变参数列表的长度
select("#",...)
end
-
运算符
- 算术运算符:+,-,*,/,%,^ - 关系运算符:==,~=,>,<,>=,<= - 逻辑运算符:and or not - 其他运算符: 1 ..:两个字符串的拼接 2 #:返回长度
-
字符串
str='abc'
str="abc"
str=[[abc]]
-
迭代器
基本定义:迭代器提供了可以迭代遍历集合元素的方法。在lua中,集合包括可以用table来表示的一切形式
基本用法
- ipairs:从前往后遍历,直到遇到一个非数字key,即停止遍历
- pairs: 不关心key的类型,从前往后的遍历
tb={'a','b'}
tb['k1']='v1'
table.insert(tb, 'c')
for k,v in ipairs(tb) do
print(k,v)
end
output:
1 a
2 b
3 c
for k,v in pairs(tb) do
print(k,v)
end
output:
1 a
2 b
3 c
k1 v1
无状态的遍历
对于无状态的遍历,in后面的表达式会返回3个值,分别是迭代函数,状态常量,控制变量,如果实际表达式的返回值不足三个的时候,那么从前往后缺的值补nil,多出的部分就会自动忽略掉。所以in后面的表达式也可以用一个由返回值的function来替代。针对于迭代并不是in后面的表达式在迭代,而是由for来控制整个迭代过程。in后面的表达式也只会在初始的时候执行一次,返回3个值。在之后的迭代过程中,真正执行的是迭代函数,而迭代函数会返回两个变量,一个是key,一个是value,之后的执行过程都是迭代函数(key,value)。例如lua的内置函数就是这样的例子。直到返回的value为nil的时候,那么就标识迭代的结束。因为key和value都返回了,所以遍历到什么位置也就知道,并不需要迭代函数内部去记录迭代的状态,这就被称为是无状态的遍历
-- 内置函数ipairs的模拟实现:
function iter(a, i):
i = i+1;
-- 当元素不是nil的时候返回
if(a[i]) then
-- 返回两个变量,key和value
return i, a[i]
end
end
function pairs(tb):
return iter, tb, 0
mytb={'a','b'}
for k,v in pairs(mytb): -- 这一步等价于for k,v in iter(mytb, 0)
-- 循环体内这里的每一步都等价于iter(mytb,k);因为这里用的索引key都是当做参数,返回值来回传递的,iter无需记录其变化过程。所以这样的迭代被称为是无状态的迭代
print(k,v)
output:
1 a
2 b
有状态的遍历
所谓的有状态的遍历,是相较于上面说的无状态的遍历而言。一般来说有状态的遍历是封装成一个闭包或者是table来使用的。即函数会自己来记录迭代的状态,即迭代到哪一步了,每一次的返回值都只有value。有状态的迭代相较于无状态的迭代来会消耗更多的资源,就如闭包创建的资源。但是有状态的遍历的好处就在于使用者不需要关心具体的遍历索引。只要拿到我们所需要的值就可以,而且因为是函数内部对状态进行处理,所以一些复杂的逻辑可以封装在函数内部。可以说是用资源的消耗来换取了操作的便利性
function iter(tb):
local index=0 -- 定义索引变量,初始值为0
local count=#tb -- 定义迭代长度变量,值为table的长度
-- 定义闭包函数
return function()
index=index+1
if index<count then
return tb[index]
end
end
end
mytb={'a','b'}
for v in iter(mytb) -- iter非闭包函数部分,如上面的local变量定义的地方,只是在这里执行一次
do
-- 在循环体内,一直循环执行的是闭包函数部分,所以闭包函数也是主要封装逻辑的地方
print(v)
end
output:
a
b
-
table
常用场景:
- 定义list - 定义k,v结构 - module的定义 - package的定义 - object的定义
基本操作:
table其实是一种k,v的数据结果。在table中,key可是是任意类型,多类型也可以同时存在在一个table中。
-- table的定义
tb = {"a","b"} -- 不指定key的时候,key默认是从0开始的自增。这样的定义也可以理解为是数组的定义
-- 增加元素
tb[2]="c"
tb["k"]="v"
table.insert (tb, "d") -- 默认是在最后面增加元素
table.insert (tb, 2, "e") -- 在索引下标为2的为值增加元素,原来的元素一次后移
-- 删除元素
tb[3]=nil
table.remove(tb) --默认删除最后一个元素
table.remove(tb,3) -- 删除指定索引为3的元素
-- table变量赋值
tb2=tb
tb=nil -- 当table通过变量赋值的时候,tb2和tb是指向同一片内存的,那么这个时候,如果把tb置为nil的时候,tb2依然可以访问到实际的变量值。因为把tb置为nil,只是取消了tb到变量值这块内存的指向,但不影响tb2,因为tb的值为nil,而且也没有其他地方使用tb,所以tb很快就会被lua的垃圾回收机制清理
-- table的排序
table.sort(tb2) -- 这里的排序是根据value来进行排序的
-- table 的value元素拼接
table.concat (tb, [, sep [, start [, end]]])
-- tb指的是拼接的table
-- sep 指的是value拼接的分隔符,可选,默认是空
-- start 指的是拼接的开始位置,可选,默认是0
-- end 指的是拼接的结束位置,可选,默认是table的长度
-
module
lua 中module即模块管理机制,可以将一些公用的代码封装在一个模块中,以API的方式提供给其他的模块使用 模块的定义本质上是一个table的定义, 由变量和函数组成,最后return table就可以 注意:模块准备提供给外部使用的变量和函数都不能定义成是local,因为local只能在模块内部局部使用
-- 模块的定义
module = {}
-- 模块中常量的定义
module.constant="this is a constant"
-- 模块中函数的定义
function module.fun2()
xxxx
end
-- 返回模块定义
return module
module的使用:
-- 引入模块
require("mymodule") as mymodule
-- 调用模块常量
print(mymodule.constant)
-- 调用模块函数
mymodule.fun2()
- 协同程序(下面简称协程)
相关定义:
协程拥有独立的堆栈,独立的局部变量,独立的指令指针
在同一时间只能有一个协程在执行,只有在明确指定协程挂起的时候他才会挂起
协程的状态:
- running:运行态
- suspend:挂起态
- dead:死亡
基本操作:
-- 创建一个协程,这个时候协程的状态是suspend,这里的参数fun在激活的时候会被调用,所使用的参数也是在resume的时候传入的。这里的fun也是协程具体的执行内容
co = coroutine.create(
function(i)
xxxx
end
)
-- 激活协程,会将协程的状态从suspend修改为running,co之后的参数都是传给create(fun)中的函数的,激活协程的时候回调用create的参数
coroutine.resume(co, i)
-- 查看协程的状态,当协程执行完成(create(fun)的参数体执行完成的时候即协程执行结束),协程的状态会从running修改为dead
coroutine.status(co)
print("--------------------------------------------------------------")
-- 创建协程的第二种方式,与第一种不同的是这里返回的不是协程,而是create的参数函数
co_fun=coroutine.wrap(
function(i)
xxxx
end
)
-- 第二种方式,直接调用函数就可以激活协程,调用函数的参数也是直接传入,
co_fun(1)
-- 返回一个正在执行的协程号
coroutine.running()
-- 协程挂起,即将running修改为suspend
coroutine.yield()
如果是使用yield()方法来暂停协程的话,那么resume()的返回值是yield的参数
coroutine.resume(co, i) ----- 8
coroutine.yield(2*4)
使用resume来激活一个因为yield而暂停的协程的时候,yield的返回值是resume的参数
函数 | 条件 | 返回值 |
---|---|---|
create | none | coroutine |
resume | 协程create之后 | true/false |
resume | 协程yield() | true/false |
resume | 协程yield(akws) | true/false,awks |
yield | 被resume(awks)激活 | awks |
通过上面的分析我们可以看到,yield和resume的交替可以实现协程内部和外部的参数传递的功能