简介
Lua 是一种强大、高效、轻量级、可嵌入的 脚本语言。它支持 过程式编程、面向对象编程、函数式编程、数据驱动编程和数据描述。Lua 将简单的过程语法与基于关联数组和可扩展语义的强大数据描述结构相结合。Lua 是 动态类型 的,通过使用基于寄存器的虚拟机解释字节码来运行(即: 需要借助虚拟机的解释才能被执行, 所以又称之为
Lua 虚拟机
),并具有 自动内存管理 和 增量垃圾收集,使其成为配置、脚本和快速原型设计的理想选择。Lua 被实现为一个库, 用干净的 C 编写, 标准 C 和 C++ 的公共子集。
作为一种扩展语言,Lua 没有“主”程序的概念: 它嵌入在主机客户端中,称为嵌入程序或简称为主机。(通常 这个宿主是独立lua程序)宿主程序可以调用函数来执行一段Lua代码,可以写和读Lua变量,可以注册C函数供Lua代码调用。通过使用 C 函数,可以增强 Lua 以应对广泛的不同领域,从而创建共享语法框架的定制编程语言。
数据类型 (八种)
number
: 数字类型, 内部以double
表示, 比如year = 2022
nil
: nil 类型, 表示没有任何有效值, 只要是没有声明的值, 它就是 nil
string
: 字符串类型, 存储文本数据, 比如str = 'hello world'
boolean
: 布尔类型, 只有两个可选值:true
(真) 和false
(假)
【 Lua 把 false 和 nil 看作是 false,其他的都为 true(包括 0 这个值,也是相当于 true)】
function
: 表示用C语言或Lua编写的方法
table
: 异构的 Hash 表, 表内可以包含任意类型的数据
userdata
: 用户(非脚本用户)定义的 C 数据结构。脚本用户只能使用它,不能定义
thread
: 表示独立的执行线程,它用于实现协同程序。
Lua 虚拟机
虚拟机相对于物理机,借助于操作系统对物理机器(CPU等硬件)的一种模拟、抽象,主要扮演CPU和内存的作用。
执行字节码中的指令,管理全局状态(
global_state
)、数据栈(StackValue
)和函数调用链状态(CallInfo
)
通过调用 c api 创建 luaL_newstate
(lauxlib.c),lua_State
结构体代表一个 Lua虚拟机,可同时创建多个,内部为单线程多实例实现,意味着各自创建的虚拟机栈相互隔离。
如下为 Lua 虚拟机体系结构图 (根据v5.4.3源码整理):
Lua源码文件通过语法词法分析(llex.c/lparser.c)生成Lua的字节码文件(指令集), 再通过Lua虚拟机解析字节码,并执行其中的指令集最后输出结果。
如下为 Lua 的解析过程:
如下为 Lua 的执行流程:
基本语法
全局变量与局部变量
-- 全局变量(不会自动销毁释放内存资源) 在代码运行周期从头到尾,都不会被销毁,而且随处都可调用。当我们代码量增加,很多时候大量新建全局变量会导致内存激增。
str = "hello world"
print(str) -- 输出: hello world
-- 获取全局变量 _G
str1 = "global"
print(_G) -- 输出: table: 0x3 [显然, _G 是个 table 类型]
print(_G['str']) -- 输出: hello world
-- 局部变量 通过 `local` 关键字标志来新建局部(临时)变量, 局部变量只在被声明的那个代码块内有效。
str2 = 'abc'
function connect()
local str2 = 1
end
connect()
print(str2) -- 输出: abc
-- 类型函数, `type` 函数能准确的确定变量的类型
print(type("hello world")) -- 输出: string
变量赋值
-- 使用等号对左边的变量进行赋值, 可以对多个变量同时赋值,变量用逗号分开,赋值语句右边的值会依次赋给左边的变量。
--[[
当左右值的数量不一致时,Lua会进行下面的设定:
变量个数 > 值的个数:按变量个数补足nil
变量个数 < 值的个数:多余的值会被忽略
]]
a, b, c = 0, 1
print(a, b, c) -- 输出: 0, 1, nil
a, b = a+1, b+1, b+2
print(a, b) -- 输出: 1, 2
a, b, c = 0
print(a, b, c) -- 输出: 0, nil, nil
交换变量
a = 34
b = 12
-- 在这里进行交换变量的操作, print(a, b) 需要输出 12, 34
a, b = b, a
print(a, b) -- 输出: 12, 34
算术运算符
--[[
+ 加法
- 减法
* 乘法
/ 除法
% 取余,求出除法的余数
^ 乘幂,计算次方
- 负号,取负值
]]
a = 21
b = 10
c = a + b
print(c) -- 输出: 31
c = a - b
print(c) -- 输出: 11
c = a * b
print(c) -- 输出: 210
c = a / b
print(c) -- 输出: 2.1
c = a % b
print(c) -- 输出: 1
c = a^b
print(c) -- 输出: 16679880978201.0
c = -a
print(c) -- 输出: -21
c = a * (b - a)
print(c) -- 输出: -231
字符串相关
字符串三种表示方式
- 单引号间的一串字符
- 双引号间的一串字符
- 双中括号的一串字符
str = '单引号间的一串字符'
str1 = "双引号间的一串字符"
str2 = [[
hello world
你好啊朋友
双中括号的一串字符
]]
转义字符, 以
\
开头的都是转义字符,下面时常用的转义字符格式
转义字符 | 含义 |
---|---|
\n |
换行(LF),将当前位置移到下一行开头 |
\r |
回车(CR),将当前位置移到本行开头 |
\|反斜杠字符\ |
|
' |
单引号 |
" |
双引号 |
\0 |
空字符(NULL) |
\ddd |
1到3位八进制数所代表的任意字符 |
\xhh |
1到2位十六进制所代表的任意字符 |
例如,如果我们想给str赋值一个单引号,一个双引号(’”),那么我们可以这样写:
str = "\'\"" -- 输出: '"
例如,新建一个变量str, 需要输出为 ab\cd"ef'g\h]]
str = "ab\\cd\"ef'g\\h]]"
print(str) -- 输出: ab\cd"ef'g\h]]
字符串拼接
s1,s2,s3 = "aaa","bbb","ccc"
all = s1 .. s2 .. s3
print(all) -- 输出: aaabbbccc
number 转 string
n = 123
s = 'm/s'
result = tostring(n)..s
print(result) -- 输出: 123m/s [某些情况下, Lua 会自动将number类型转换成string类型]
string 转 number
n = 123
s = '456'
result = tonumber(n) + tonumber(s)
print(result) -- 输出: 579
-- 相加的值都是数字值时候其实不用转换也是可以相加操作的, 但是会得到一个带小数的值
result = n + s
print(result) -- 输出: 579.0
关系运算符
符号 | 含义 |
---|---|
== |
等于,检测两个值是否相等,相等返回 true,否则返回 false |
~= |
不等于,检测两个值是否相等,相等返回 false,否则返回 true |
> |
大于,如果左边的值大于右边的值,返回 true,否则返回 false |
< |
小于,如果左边的值大于右边的值,返回 false,否则返回 true |
>= |
大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false |
<= |
小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false |
a = 21
b = 10
print(a==b) -- 输出: false
print(a~=b) -- 输出: true
print(a>b) -- 输出: true
print(a<b) -- 输出: false
print(a>=b) -- 输出: true
print(a<=b) -- 输出: false
a = 1
b = '1'
c = a
d = 2
print(a == b) -- 输出: false [说明不同类型间的对比即使值相同也是不相等的]
print(c == a) -- 输出: true
print(a ~= b) -- 输出: true
print(d <= c) -- 输出: false
逻辑运算符
逻辑运算符基于布尔型的值来进行计算, 并给出结果。
符号 | 含义 |
---|---|
and |
逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B |
or |
逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B |
not |
逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false |
print(true and false) -- 输出: false
print(true or false) -- 输出: true
print(true and true) -- 输出: true
print(false or false) -- 输出: false
print(not false) -- 输出: true
print(123 and 345) -- 输出: 345
print(nil and true) -- 输出: nil
a = 1
b = '1'
c = 0
print(a and b) -- 输出: 1
print(c or a) -- 输出: 0
print(not b) -- 输出: false
print(d and c) -- 输出: nil
print(1 < 2 and 3 > 2) -- 输出: true
条件判断
--[[
单条件判断语法结构:
if 条件 then
符合条件的代码
end
]]
n = 5
if n < 10 then
print('n 小于 10')
end
--[[
多条件判断语法结构:
if 条件1 then
满足条件1
elseif 条件2 then
不满足条件1,但是满足条件2
else
前面条件全都不满足
end
]]
n = 2
if n >= 0 and n < 5 then
print('太小')
elseif n >= 5 and n < 10 then
print('适中')
elseif n >= 10 then
print('太大')
end -- 输出: 太小
循环语句
--[[
while 循环语法结构:
while 继续循环判断依据 do
执行的代码
end
]]
-- 计算从1加到100
result = 0
num = 1
while num <= 100 do
result = result + num
num = num + 1
end
print(result) -- 输出: 5050
--[[
for 循环语法结构:
for 临时变量名=开始值,结束值,步长 do
循环的代码
end
步长可以省略,默认为1
]]
result = 0
for i = 0, 100 do
result = result + i
end
print(result) -- 输出: 5050
--[[
中断循环语句: 比如循环运行到一半, 不想继续运行了(跳出本次循环), 可以使用 break 关键字
]]
result = 0
for i = 1, 100 do
result = result + i
if i == 100 then
result = result - i
end
end
print(result) -- 输出: 4950
迭代器泛型
迭代器 ipairs
是一个构造,可以用来遍历集合或容器的元素。 在Lua中,这些集合通常引用表,这些表用于创建各种数据结构,如数组。
迭代器泛型提供集合中每个元素的键值对。
for key, value in ipairs({"hello", "world"}) do
print(key, value)
end
--[[
输出:
1 hello
2 world
]]
函数使用
--[[
语法结构:
function 函数名(参数1(可选),参数2(可选),...)
函数代码内容
end
]]
function hello()
return "hello world"
end
print(hello()) -- 输出: hello world
Table 表
很多语言中都有 数组 这个概念,在 Lua 中,我们可以使用 table
(表)来实现这个功能 (table
就是当成 Lua 中的数组使用)。table
是一个一系列元素的集合(里面元素可以是任意类型数据
, 包括 function
),使用大括号进行表示,其中的元素之间以逗号分隔,类似下面的代码:
t = {1,3,8,5,4}
我们可以直接使用元素的下标,来访问、或者对该元素进行赋值操作。
注意
元素的开始下标是从1
开始的, 这个和其他很多语言从0
开始的下标不一样。
一般语言中的数组基本都为 不可变长度,这里的table
为 可变长度 。
t = {1,3,8,5,4}
print(t[0]) -- 输出: nil
print(t[1]) -- 输出: 1
-- 自定义下标
t = {
["apple"] = 10,
banana = 12,
pear = 6,
}
print(t.apple) -- 输出: 10
print(t["pear"]) -- 输出: 6
-- 使用 `["下标"] = 值` 和 `下标 = 值` 都是正确写法。当第二种方式有歧义时,应该用第一种方式
t1 = {3, 2, 4}
--[[
上面 t1 的代码等价于:
t1 = {
[1] = 3,
[2] = 2,
[3] = 4,
}
甚至你可以跳过某些下标, 如 `t2 = {[2] = 5}`
]]
将元素是string
或者number
类型的table
,每个元素连接起来变成字符串并返回。
--[[
基本语法: table.concat (table [, sep [, i [, j ] ] ])
可选参数sep,表示连接间隔符,默认为空。
i 和 j 表示元素起始和结束的下标。
]]
local a = {1, 3, 5, "hello" }
print(table.concat(a)) -- 输出: 135hello
print(table.concat(a, "|")) -- 输出: 1|3|5|hello
也可以对 table
元素进行插入、删减和移动
--[[
插入基本语法: table.insert (table, [pos ,] value)
在(数组型)表 table 的 pos 索引位置插入 value,其它元素向后移动到空的地方。pos 的默认值是表的长度加一,即默认是插在表的最后。
]]
local a = {1, 8} -- a[1] = 1, a[2] = 8
table.insert(a, 1, 3) -- 在表索引为1处插入3
print(a[1], a[2], a[3]) -- 输出: 3 1 8
table.insert(a, 10) -- 在表的最后插入10
print(a[1], a[2], a[3], a[4]) -- 输出: 3 1 8 10
--[[
删除基本语法: table.remove (table [, pos])
在表 table 中删除索引为 pos(pos 只能是 number 型)的元素,并返回这个被删除的元素,它后面所有元素的索引值都会减一。pos 的默认值是表的长度,即默认是删除表的最后一个元素。
]]
local a = {1, 2, 3, 4}
print(table.remove(a, 1)) -- 删除索引为1的元素, 返回被删除的元素值为1
print(a[1], a[2], a[3], a[4]) -- 输出: 2 3 4 nil
print(table.remove(a)) -- 删除最后一个元素, 返回被删除的元素值为4
print(a[1], a[2], a[3], a[4]) -- 输出: 2 3 nil nil
--[[
移动基本语法: table.move (a1, f, e, t [,a2])
将元素从 table 移动a1到 table a2,执行与以下多重赋值等效的操作: a2[t],··· = a1[f],···,a1[e]. 的默认a2值为a1。目标范围可以与源范围重叠。要移动的元素数量必须适合 Lua 整数。
返回目标表a2。
]]
t = {1, 2, 4, 5, 6, 7, 8}
b = {}
table.move(t, 3, 5, 1, b)
for k,v in ipairs(b) do
print(k)
end
--[[
输出:
1
2
3
]]
还可以对 table
根据可选的比较器参数对表进行排序
--[[
排序基本语法: table.sort (table [, comp])
对一个长度为 length=n 的数组 table 排序,也就是对 tab_table[1] 到tab_table[n] 排序,如果参数 comp 不省略,则它必须是一个函数,可以接收表tab_table 的两个元素,并且在第一个元素小于第二个元素时返回 true,其他情况返回 false,如果省略参数 comp,则 Lua 彼岸准运算符 operator < 将会被使用。
]]
fruits = {"banana", "red", "orange", "blue", "apple", "grapes"}
table.sort(fruits)
for k,v in ipairs(fruits) do
print(k,v)
end
--[[
输出:
1 apple
2 banana
3 blue
4 grapes
5 orange
6 red
]]
最后还能获取 table
的大小
--[[
基本语法: #table
在旧版本的 Lua5.1 前, 使用的是 table.getn() 方法 (个人认为 getn 方法更加人性化、更好理解)。
]]
t = {1, 2, 4, 5}
print(#t) -- 输出: 4
评论一下?