Lua简介
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。是巴西里约热内卢天主教大学里的一个研究小组于 1993 年开发的。
设计目的
其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
特性
- 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
- 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
- 其他特性:
- 支持面向过程编程和函数式编程;
- 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
- 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
- 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
应用场景
- 游戏开发
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
- 安全系统,如入侵检测系统
安装Lua
Mac 系统
1 | tar zxf lua-5.3.0.tar.gz |
或者
1 | brew install lua |
Windows 系统
下载地址:https://github.com/rjpcomputing/luaforwindows/releases,
双击安装后即可在该环境下编写 Lua 程序并运行。
windows下你可以使用一个叫”SciTE”的IDE环境来执行lua程序。
变量
变量定义
Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
1 | a = 5 -- 全局变量 |
do … end这种表达方式只是一种定义局部变量和执行一个语句块的组合,没有其他特殊含义。
变量赋值
Lua 可以对一个变量进行赋值,也可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
1 | a = 1 |
数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
数据类型 | 类型描述 |
---|---|
nil | 只有值nil属于该类,表示一个无效值(在条件表达式中相当于false) |
boolean | 包含两个值:false和true |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
table | Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表 |
function | 由 C 或 Lua 编写的函数 |
thread | 表示执行的独立线路,用于执行协同程序 |
userdata | 表示任意存储在变量中的C数据结构 |
我们可以使用 type 函数测试给定变量或者值的类型:
1 | print(type("Hello world")) --> string |
nil(空)
- nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值
- 对于全局变量和 table,nil 还有一个”删除”作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:
1
2
3
4
5a=1
print(a)
a=nil
print(a)
print(b) - **nil 作比较时应该加上双引号 “**,试下如下代码type(X)==nil 结果为 false 的原因是 type(X) 实质是返回的 “nil” 字符串,是一个 string 类型,不信你执行一下
1
2type(X)==nil
type(X)=="nil"type(type(X))
boolean(布尔)
boolean 类型只有两个可选值:true和 false,Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:
1 | if false or nil then |
number(数字)
Lua 默认只有一种 number 类型 – double(双精度)类型,以下几种写法都被看作是 number 类型:
1 | print(type(2)) |
string(字符串)
字符串的定义
- 字符串由一对双引号或单引号来表示:
1
2string1 = "this is string1"
string2 = 'this is string2' - 也可以用 2 个方括号 “[[]]” 来表示”一块”字符串:
1
2
3
4
5
6
7
8
9html = [[
<html>
<head></head>
<body>
<a href="http://www.baidu.com/">百度</a>
</body>
</html>
]]
print(html) - 在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字,试运行如下代码:
1
print("2" + 6)
字符串常见的操作
Lua 提供了很多的方法来支持字符串的操作:
字符串全部转为大写字母。
1
string.upper('lua')
字符串全部转为小写字母。
1
string.lower('LUA')
在字符串中替换。
1
string.gsub("aaa","a","z",3); -- aaa要操作的字符串;a 被 z 替换 3 次 (如果忽略3,则表示全部替换)
在一个指定的目标字符串中搜索指定的内容,返回其具体位置。不存在则返回 nil。
1
string.find("Hello Lua user", "Lua")
返回一个类似printf的格式化字符串
1
2
3string.format("the value is:%d",4) -- %d, %i 接受一个数字并将其转化为有符号的整数格式
string.format("the value is:%f",9.990000) -- %f 接受一个数字并将其转化为浮点数格式
string.format("the value is:%s",'a') -- %s 接受一个字符串并按照给定的参数格式化该字符串计算字符串长度。
1
2
3str = 'abc'
string.len(str) 或者
print(#str)连接两个字符串,使用的是 ..
1
print("abc".."xyz")
字符串截取
1
2str='m.jd.com'
string.sub(str, 4, -1) -- str要操作的字符串;4 截取开始的位置;截取结束位置,默认为 -1,最后一个字符。
table(表)
table(表)的定义
在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
1 | -- 创建一个空的 table |
Lua 中的表(table)其实是一个”关联数组”,数组的索引可以是数字或者是字符串:
1 | a = {} |
不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引默认以 1 开始。
1
2
3
4local tbl = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl) do
print("Key", key)
endtable 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
1
2
3
4
5
6
7
8
9
10a = {}
for i = 1, 5 do
a[i] = i
end
for key, val in pairs(a) do
print(key, val)
end
a["key1"] = "val"
print(a["key1"])
print(a["key2"])
table(表)的操作
- Table 连接
- 插入和移除
- Table 排序
- Table 最大值
Table 连接
可以使用 concat() 输出一个列表中元素连接成的字符串:
1 | fruits = {"banana","orange","apple"} |
插入和删除
可以使用 insert() 和 remove() 来分别实现 table的插入和删除:
1 | fruits = {"banana","orange","apple"} |
Table 排序
使用sort() 方法对 Table 进行排序,默认排序顺序是根据字符串Unicode码点。
1 | fruits = {"banana","orange","apple","grapes"} |
Table 最大值
table.maxn 在 Lua5.2 之后该方法已经不存在了。
function(函数)
在 Lua 中,函数是被看作是”第一类值”,函数可以存在变量里。
第一类值表示函数与其他传统类型的值(例如数字和字符串类型)具有相同的权利。即函数可以存储在变量中,可以作为实参传递给其他函数,还可以作为其他函数的返回值,如下两段代码:
1 | --函数可以存储在变量中: |
1 | --以匿名函数的方式通过参数传递: |
函数的定义
1 | optional_function_scope function function_name( argument1, argument2, argument3..., argumentn) |
- optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
- function_name: 指定函数名称。
- argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
- function_body: 函数体,函数中需要执行的代码语句块。
- result_params_comma_separated: 函数返回值,Lua函数可以返回多个值,每个值以逗号隔开。
示例
- 以下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:myprint = function(param)
1
2
3
4
5
6
7
8
9
10
11
12
13
14function max(num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
-- 调用函数
print("两值比较最大值为 ",max(10,4))
```
- 可以将函数作为参数传递给函数,如下实例:
print(“这是打印函数 - ##”,param,”##”)
end
function add(num1,num2,functionPrint)
result = num1 + num2
– 调用传递的函数参数
functionPrint(result)
end
– myprint 函数作为参数传递
add(2,5,myprint)
1 |
|
– 获取table中的最大值
function maximum (a)
local mi = 1 – 最大值索引
local m = a[mi] – 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5}))
1 |
|
– 求平均值
function average(…)
result = 0
local arg={…} – arg 为一个表,局部变量
for i,v in ipairs(arg) do
result = result + v
end
print(“总共传入 “ .. #arg .. “ 个数”)
return result/#arg
end
print(“平均值为”,average(10,5,3,4,5,6))
1 |
|
co = coroutine.create(
function(i)
print(i);
end
)
coroutine.resume(co, 1) – 1
print(coroutine.status(co)) – dead
print(“————-“)
co2 = coroutine.create(
function()
for i=1,10 do
print(i)
if i == 3 then
print(coroutine.status(co2)) –running
print(coroutine.running()) –thread:XXXXXX
end
coroutine.yield()
end
end
)
coroutine.resume(co2) –1
coroutine.resume(co2) –2
coroutine.resume(co2) –3
print(coroutine.status(co2)) – suspended
print(coroutine.running())
1 | > 当create一个coroutine的时候就是在新线程中注册了一个事件。当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。 |
function foo (a)
print(“foo 函数输出”, a)
return coroutine.yield(2 * a) – 返回 2*a 的值
end
co = coroutine.create(function (a , b)
print(“第一次协同程序执行输出”, a, b) – co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)
print(“main”, coroutine.resume(co, 1, 10)) – true, 4
print(“–分割线—-“)
print(“main”, coroutine.resume(co, “r”)) – true 11 -9
print(“—分割线—“)
print(“main”, coroutine.resume(co, “x”, “y”)) – true 10 end
print(“—分割线—“)
print(“main”, coroutine.resume(co, “x”, “y”)) – cannot resume dead coroutine
print(“—分割线—“)
1 |
|
local newProductor
function productor()
local i = 0
while true do
i = i + 1
send(i) – 将生产的物品发送给消费者
end
end
function consumer()
while true do
local i = receive() – 从生产者那里得到物品
print(i)
end
end
function receive()
local status, value = coroutine.resume(newProductor)
return value
end
function send(x)
coroutine.yield(x) – x表示需要发送的值,值返回以后,就挂起该协同程序
end
– 启动程序
newProductor = coroutine.create(productor)
consumer()
1 |
|
array = {“a”, “b”}
print(#array)
1 | ### 如果不是数组,使用#是不能获取table长度的,可以使用如下方法进行获取: |
tabletest = {a=1,b=2,c=3,d=4,e=5}
local count=0
for k,v in pairs(tabletest) do
count = count + 1
end
print(count)
1 |
|
array = {“a”, “b”}
for i= 0, #array do
print(array[i])
end
1 |
|
array = { {1,2},{‘a’,’b’},3,’c’ }
for i=1, #array do
if (type(array[i]) == ‘table’) then
for j=1, #array[i] do
print(array[i][j])
end
else
print(array[i])
end
end
1 |
|
–[ 0 为 true ]
if(0)
then
print(“0 为 true”)
end
1 | ## if...else 语句 |
a = 100;
if( a < 20 )
then
print(“a 小于 20” )
else
print(“a 大于 20” )
end
1 | ## if 嵌套语句 |
a = 100;
b = 200;
if( a == 100 )
then
if( b == 200 )
then
print(“a 的值为 100 b 的值为 200” );
end
end
1 |
|
for var=exp1,exp2,exp3 do
<循环体>
end
1 | > var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。 |
for i=1,10 do
print(i)
end
1 | ### 泛型for循环 |
–打印数组a的所有值
a = {“one”, “two”, “three”}
for i, v in ipairs(a) do
print(i, v)
end
1 | > i是数组索引值,v是对应索引的元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。 |
while(循环条件)
do
<循环体>
end
1 | ``` |
循环嵌套
以下实例使用了for循环嵌套:
1 | for m=1,9 do |
循环控制语句
循环控制语句用于控制程序的流程, 以实现程序的各种结构方式。
- break
- goto
break
Lua 编程语言 break 语句插入在循环体中,用于退出当前循环或语句,并开始脚本执行紧接着的语句。
如果你使用循环嵌套,break语句将停止最内层循环的执行,并开始执行的外层的循环语句。1
2
3
4
5
6
7
8for i=1,10 do
if(i>5)
then
break
else
print(i)
end
endgoto
goto 语句允许将控制流程无条件地转到被标记的语句处。
语法格式如下所示:Label 的格式为:1
goto label
1
:: Label ::
以下实例中使用了 goto:
1 | for i=1, 3 do |