1、介绍
Lua是一种轻量级的脚本语言,具有以下特点:
简单易学:Lua语法简单,易于学习和使用。
高效性:Lua的解释器非常快,可以在很短的时间内执行大量的代码。
可嵌入性:Lua可以嵌入到其他应用程序中,作为脚本语言使用。
可扩展性:Lua可以通过编写C语言扩展库来扩展其功能。
功能丰富:Lua提供了许多有用的功能,如面向对象编程、闭包、协程等。
跨平台性:Lua可以在多个平台上运行,包括Windows、Linux、Mac OS等。
2、运行环境安装(linux版)
下载lua源码包
wget http://www.lua.org/ftp/lua-5.4.0.tar.gz
![](https://i-blog.csdnimg.cn/blog_migrate/d150760468f25b793d3d98a5f8b78043.png)
解压文件
tar zxf lua-5.4.0.tar.gz
![](https://i-blog.csdnimg.cn/blog_migrate/3115a37a9f865ff0ad8d592ef4fdbc66.png)
进入解压后的文件中,编译文件
make linux test
![](https://i-blog.csdnimg.cn/blog_migrate/8f4309e642b587784ca6a331ea12e2ef.png)
编译成功后,会在src目录里面看到lua.c和luac.c文件
安装lua
make install
![](https://i-blog.csdnimg.cn/blog_migrate/c6b557eb6589c18cad260027122f7def.png)
刷新动态库链接
ldconfig
验证是否安装成功
lua -v
![](https://i-blog.csdnimg.cn/blog_migrate/dfb75da3b76db102a15cb83c7b1bf9a3.png)
3、lua的使用
基础语法
命名规则
lua中变量的命名规则和Java类似,可以由大小写字母、数字、下划线组成,但不能以数字开头,并且lua的内置关键字不能命名为变量
ab
ab12
_ab
ab_12
aA
AB
内置关键字
and | break | if | then | elseif | else |
end | while | do | false | true | function |
local | return | repeat | until | in | or |
not | nil | goto | for |
分号使用
lua语句的结尾,分号的使用是可选的
a = 1;
b = 2;
c = 3
d = 4
注释规则
单行注释:两个连续的减号为单行注释
多行注释:两个连续减号,再加上两对方括号(注意:多行注释两个连续的--后面不能跟空格,否则会被认为是单行注释)
-- 此行为注释内容
a = 1
--[[
多行注释内容
]]
b = 2
全局变量和局部变量
在lua中使用local修饰的变量为局部变量,其它的变量都为全局变量
-- 全局变量i
i = 10;
do
-- 局部变量x
local x = i - 1;
i = i - 1;
-- 局部代码块打印x
print(x);
end
-- 打印i
print(i);
-- 局部代码块之外打印x
print(x);
可以看到x第一次正常打印出来了,x第二次打印的却是nil,这是因为x是局部变量,到外界被释放了
判断语句
--语法
if 表达式1 then
语句块1
elseif 表达式2 then
语句块2
else
语句块3
end
function panduan(a)
if(a>100 or a<0) then
print("输入不合法,请重新输入");
elseif(a>=80) then
print("优秀");
elseif(a>=60) then
print("及格");
else
print("不及格");
end
end
print("please enter a number:");
n=io.read("*n");
panduan(n);
![](https://i-blog.csdnimg.cn/blog_migrate/d82860eecef38791ed5e884cd7fa80dc.png)
循环语句
--语法一
while 循环条件(符合该循环条件则继续循环) do
循环体
end
--语法二
repeat
循环体
until 循环条件(符合该循环条件,则退出循环)
--语法三
for 变量=初始值,终止值,步长 do
循环体
end
function whiletest(n,sum)
local i = 0;
while i<=n do
sum=sum+i;
i=i+1;
end
return sum;
end
function fortest(n,sum)
for i=0,n,1 do
sum=sum+i;
end
return sum;
end
function repeattest(n,sum)
local i = 0;
repeat
sum=sum+i;
i=i+1;
until i>n
return sum;
end
print("Please enter a number:");
n=io.read("*n");
print("while:add from 1 to "..n.." and the result is: "..whiletest(n,0));
print("for:add from 1 to "..n.." and the result is: "..fortest(n,0));
print("repeat:add from 1 to "..n.." and the result is: "..repeattest(n,0));
![](https://i-blog.csdnimg.cn/blog_migrate/f7f2a01f1985b4ae62a4120a1d387a89.png)
数据类型
lua有8种数据类型
分别为:nil(空),boolean(布尔),number(数值),string(字符串),userdata(用户数据),function(函数),thread(协程),table(表)
使用type()函数可以获取到值的类型名称
![](https://i-blog.csdnimg.cn/blog_migrate/eab3daaf8c5e9bb0af24128791103198.png)
nil
nil是一种只有nil值的空类型,lua使用nil来表示无效值的情况,表示未被定义
boolean
boolean类型只有两个值:true和false
逻辑运算符
nil默认为false
and: 前面的操作数如果为false,就返回前面的操作数;否者返回后面的操作数
or: 前面的操作数为true,则返回前面的操作数,否者返回后面的
not: 取反
![](https://i-blog.csdnimg.cn/blog_migrate/c2949a43dd426ec1670931d2280b6d71.png)
number
lua支持整型以及双精度的浮点型,整型被称为integer,浮点型被称为float,可以使用math.type()查看当前数值是类型
![](https://i-blog.csdnimg.cn/blog_migrate/35d10968808f0bfd1862f0c0683f1e8f.png)
lua支持:加(+)、减(-)、乘(*)、除(/)、取负(-)、取模(%)、取整除(//)、幂运算(^)
注意:进行运行的时候,只有运算符号前后两个操作数都是整型并且不是除法时,结果才是整型,否则就是浮点型
![](https://i-blog.csdnimg.cn/blog_migrate/6c9011218ba4037cffd8660c3454716b.png)
lua对运算还提供了math数学库,与Java中的Math类似,可以对数据进行各种运算
![](https://i-blog.csdnimg.cn/blog_migrate/f8175a0c31eba0a7032a3704a83f8ce5.png)
例子:红包分配算法
t={}
function initAmount(m,n)
local a = math.random(1,2*(math.floor(m/n)));
if n>1 and m<n then
initAmount(m,n);
elseif n==1 then
t[n]=m/100;
m = 0;
else
t[n]=a/100;
m = m-a;
n = n-1;
initAmount(m,n);
end
end
print("please enter an amount:");
amount = io.read("*n");
print("please enter share number:")
number = io.read("*n");
initAmount(amount*100,number);
print("the amount allocated is:");
for i = 1,number do
print(t[i])
end
![](https://i-blog.csdnimg.cn/blog_migrate/365f19331820778831c2ac33c3c6338b.png)
string
lua中使用双引号或单引号来定义字符串变量,比如 a = "abc"或b = 'def'
并且b='def"abc"',单引号中包含双引号也可以正常输出,双引号不需要转义
但双引号中包含双引号,无法正常输出,这时候的双引号需要转义
![](https://i-blog.csdnimg.cn/blog_migrate/879788adf5e6b5639239ea5f3ec05fb1.png)
字符串的相关操作
#后面跟上字符串,会返回当前字符串的长度
字符串使用..进行拼接,如果拼接的是数值,会自动将其转为字符串
多行字符串使用两对中括号包裹,a=[[字符串]],注意,使用该方式,如果字符串中有转义字符,无法转义
lua提供了数值和字符串的自动转换,在运算时,如果运算式中包含字符串,会自动尝试将其转为数值类型,转换不了,会报错(注意:比较运算符不会强制类型转换,因此当比较符前后同时包含数值和字符串时,会报错)
显式类型转换,lua提供了主动的类型转换,tonumber(字符串)将字符串转为数值,tostring(数值)将数值转为字符串
lua提供了一个字符串库string,提供了很多功能处理字符串,调用示例:string.len(字符串)或字符串:len()
![](https://i-blog.csdnimg.cn/blog_migrate/46df5fce6de4186215a14ea6c39b57da.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6fedf7cbf5e3df76be785cd25afef555.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2b7c74cb161f51fd287a0e38e7ec27bc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/86445f2d9b0ad31012bfcc003668aae1.png)
function
function类型定义的代码块,为函数区,可以通过函数名进行调用
--语法
function 函数名(入参1,入参2,...)
具体代码
end
--调用方式
函数名(参数1,参数2,...)
table
表是lua中常用的一直数据结构,可以用来表示数组、集合等等
使用a = {}构造表a,直接输出a会返回表的地址,a只是表的引用
表添加元素采用的是a[key] = value,key-value的方式
当多个表名引用同一张表时,任何一个引用改变了表数据,其它引用的表数据也会改变
当表的所有引用都被释放时,垃圾回收器才会删除这张表
将nil给表元素,可以将表元素删除
![](https://i-blog.csdnimg.cn/blog_migrate/93944a6b93c7da7dcf12f393d3d74e5c.png)
当key是字符串时,可以通过表名.key名=value的方式插入数据,可以使用表名.key名的方式调用表元素等价于表名[key名]
a[0]和a["0"]是两个不一样的表元素,因为key一个是数值类型一个是字符串类型;但a[1]和a[1.0]指代的是同一个元素,因为整型和浮点型之间会进行类型转换,a[1.0]会转化成a[1]
![](https://i-blog.csdnimg.cn/blog_migrate/e90ac981dbd518e9a8562e0c5a1c94e7.png)
表的构建方法有:空构建,列表式构建,记录式构建,列表和记录混合构建
-- 表构建,相当于list={[1]="a",[2]="b",[3]="c",[4]="d",[5]="e",[6]="f"}
list={"a","b","c","d","e","f"}
-- 记录构建,相当于entity={["id"]=1,["name"]="zhangsan",["age"]=23}
entity={id=1,name="zhangsan",age=23}
-- 混合构建,相当于mix={["id"]=2,[1]="a",[2]="b",["name"]="lisi",["age"]=23,[3]="c"}
mix={id=2,"a","b",name="lisi",age=23,"c"}
print("list:first element is "..list[1]);
print("entity:name is "..entity.name);
print("mix:first element is "..mix[1].." name is "..mix.name);
![](https://i-blog.csdnimg.cn/blog_migrate/27ee0bdeb0ab1cc77c5ae4b8f7eb8cdb.png)
pairs迭代器和ipairs迭代器
table={1,2,3,id="10021","a","b",name="ls"}
print("pairs result is:");
-- pairs可以遍历出所有的元素,但是遍历的元素不是按顺序的
for k,v in pairs(table) do
print(k,v);
end
print("ipairs result is:")
--[[ipairs只能遍历序列(key从1开始,直到key不连续为止),
输出顺序会按定义的顺序输出]]
for k,v in ipairs(table) do
print(k,v)
end
![](https://i-blog.csdnimg.cn/blog_migrate/17a0c5bf1bfa5a9ec3368138d69f6642.png)
table表标准库
t={1,2,3,4}
-- 在表t开头插入9,其它元素依次后移一位
table.insert(t,1,9);
print("insert(t,1,9)");
for k,v in pairs(t) do
print(k,v)
end
-- 在表t末尾插入10
table.insert(t,10);
print("insert(t,10)");
for k,v in pairs(t) do
print(k,v)
end
-- 移除表t第一个元素,其它元素依次前移一位
table.remove(t,1);
print("remove(t,1)");
for k,v in pairs(t) do
print(k,v)
end
-- 移除表t最后一位元素
table.remove(t);
print("remove(t)")
for k,v in pairs(t) do
print(k,v)
end
![](https://i-blog.csdnimg.cn/blog_migrate/314f94e9d7e59cd86902cb748fd48adc.png)
userdata
userdata叫做用户数据也叫用户自定义数据,其作用是为了与其它编程语言(例如C语言、C++、C#)进行交互。
thread
lua中的thread叫做协同程序,与线程比较类似,拥有独立的堆栈,独立的局部变量,独立的指令指针。
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。线程是操作系统进行调度的最小单位,而协程则是在应用程序内部进行调度的最小单位。在多线程并发编程中,线程之间的切换涉及到内核态和用户态之间的切换,会带来一定的开销。而在协程并发编程中,由于很多协程都运行在同一个线程内,因此协程之间的切换只涉及到用户态之间的切换,避免了内核态和用户态之间的频繁切换,从而提高了性能
coroutine.create创建一个协程,返回的是corontine,参数是一个函数
coroutine.resume重新启动协程,配合create使用
coroutine.yield挂起协程
coroutine.status返回当前协程状态,suspended(挂起)、running(运行)、dead(死亡)
coroutine.wrap创建一个协程,返回一个函数,一旦调用这个函数,就会进入coroutine,和create类似。
coroutine.running返回正在运行的coroutine,返回的是coroutine的线程号
function run(data)
-- 当前协程状态
print("1:"..coroutine.status(co));
data = data+1;
print("co", 1, data);
-- 当前协程的线程号
print(coroutine.running());
-- 当前协程挂起
coroutine.yield()
print("2:"..coroutine.status(co));
print(coroutine.running())
data = data+1;
print("co", 2, data)
end
function test(data)
-- 当前协程的线程号
print(coroutine.running())
data = data+1;
print("co2",3,data)
-- 协程挂起
coroutine.yield()
print(coroutine.running())
data = data+1;
print("co2",4,data)
end
print("coroutine.create():");
-- create方式创建协程
co = coroutine.create(run);
print("create status is:"..coroutine.status(co));
print("coroutine.wrap():");
-- wrap方式创建协程
co2 = coroutine.wrap(test);
--print("wrap status is:"..coroutine.status(co2));
data = 1
-- 启动co协程
coroutine.resume(co, data)
-- 启动co2协程
co2(data);
print("co first resume end status:"..coroutine.status(co));
print("main", data);
-- 重新启动co协程
coroutine.resume(co, data)
print("co second resume end status:"..coroutine.status(co));
-- 启动死亡协程,会报出cannot resume dead coroutine
print("co third resume:",coroutine.resume(co,data));
-- 重新启动co2协程
co2(data);
![](https://i-blog.csdnimg.cn/blog_migrate/8a85b5ea9b9b056db4c6371e2c9e8bd6.png)
本章内容到此结束,下一章Redis运行Lua脚本和Redis配合Lua完成秒杀系统