Lua基础

5.9k 词

Lua是一种灵活小巧的脚本语言,可以被嵌入应用程序中,为应用程序提供灵活的扩展和定制功能。

注释

单行注释

1
2

print("Hello World!") -- 另一个单行注释

多行注释

1
2
3
4
--[[
多行注释
另一个多行注释
--]]

变量

变量标识符

Lua的变量标识符用大小写字母、下划线、数字等组成,必须用字母或下划线作为开头。最好不要使用下划线加大写字母的标示符,以免与Lua的保留字冲突。在Lua的变量标识符中,字母需要区分大小写。一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION等)被保留用于 Lua 内部全局变量,_G表示全局作用域,后边还会提到。

变量作用域

Lua中作用域分为三种:全局作用域、局部作用域、表内作用域。声明变量时会默认使用全局作用域;声明局部变量需要显式的在变量名之前加上Local关键字。表(table)中存储的是键值对,可以根据键名来获取对应的值。

1
2
3
4
5
var1 = 1  -- 全局
local var2 = 2 -- 局部
table1= {}
var3 = "key3"
table1.var3 = 3 -- 表内

全局变量可以看做在名为_G的表中的变量。

基本数据类型

在Lua中使用变量不需要提前声明,变量的类型决定于用户赋值的类型。可以使用type()函数判断变量的类型。

Lua中有8种基本的数据类型:

  • nil
  • boolean
  • string
  • number
  • table
  • function
  • thread
  • userdata

nil

nil类型表示没有有效值,当访问一个没有初始化的全局变量时并不会出错,而会得到nil
通过将全局变量和表内的变量赋值为nil,可以达到释放该变量的目的。

1
2
-- 引用未定义变量将返回 nil ,并不会出错
foo = anUnknownVariable -- 等价于 foo = nil

boolean

boolean 类型只有两个可选值:truefalse,在Lua中falsenil被看作是“假”,其他的都为“真”。

string

与python类似,字符串可以使用两个单引号'或两个双引号"包裹起来表示,也可以用多行注释来表示跨行的字符串。

1
2
3
4
5
6
7
str = 'aspythonstring' -- 像 Python 一样不可变
str = "aspythonuse" -- 可以双引号
str = [[
像 Python 的多行注释可用于
表示多行字符串一样
方便
]]

对于[[ ... ]]形式的字符串,可以两个方括号之间加相等数量的=,如:

1
2
3
str = [=[
[[[[[[[字符串的内容就是 好多方括号]]
]=]

使用 ..来连接两个字符串:

1
print("a" .. 'b')      -- ab

使用#来获取字符串长度

1
2
str = "hello"
print(#str) -- 5

字符串的一些操作其他操作

1
2
3
4
5
6
7
8
9
10
11
12
string.upper(argument)  -- 将所有的小写字母转为大写
string.lower(argument) -- 将所有的大写字母转为小写
string.gsub(mainString,findString,replaceString,num)
-- 替换操作 将mainString中的findString替换为replaceString,num表示替换的次数,如不指定则表示替换全部
string.strfind (str, substr, [init, [end]])
-- 在字符串str中搜索指定的substr,返回起止的索引,不存在则返回nil
string.reverse(arg) -- 反转字符串
string.format(...) -- 返回格式构造的字符串 如 string.format("the value is:%d",4)
string.char(arg) -- 将整型数字转成字符并连接,可接收多个字符作为参数
string.byte(arg[,int]) -- 转换字符为整数值 arg是字符串时转换第int个值,如int未指定则默认第一个字符
string.len(arg) -- 字符串长度
string.rep(string, n)) -- 将string重复n次拼接起来

number

Lua中的number都是双精度的实数值。整数同样也用number表示。

table

Lua中的table是一种关联数组(associative array),其中存储的是键值对。也可以仅指定值而使用默认的数字索引作为键。Lua的表中默认初始索引从 1 开始。table是一种非常重要的数据类型,后边专用一节细讲。

function

表示由C或Lua编写的函数。Lua中的function是“第一类值”,可以存储在变量中,可以作为参数或返回值,后边详细介绍。

thread

表示执行的独立线路,用于执行协同程序(coroutine)。

userdata

表示任意存储在变量中的C数据结构。

控制流

Lua中的代码块以总是以end结尾(函数的定义也用end表示结束)。在Lua中有以下一些流程控制语句。

分支语句

使用if ... then ... else来表示分支结构。除了nilfalse之外,其它的值(包括数字0和空字符串''等)均为“真”。

逻辑的与、或、非用andornot表示,判断相等、不等使用==~=

if判断可以多分支和嵌套,相关语句使用方式参见下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
if a <= 0
then
print("a <= 0")
if b > 0
then
print("b > 0")
end
elseif a >= 100
then
print("a >= 100")
else
print("0 < a < 100")
end

某些情况下可以使用以下的方式来达到C语言中问号表达式的效果:

1
2
ans = aBoolValue and 'yes' or 'no'
-- 对于表达式 a ? b : c 需注意排除 b = false 或 nil 的情况

循环语句

Lua中的循环有以下四种方式:

while…do循环

与C语言中相似,详见下边代码,注意在Lua中没有自增和自减运算符:

1
2
3
while num < 50 do
num = num + 1
end

for循环

for循环指定起止的数值及步长,循环变量可以取到起止的值,当不指定迭代步长时步长使用默认值1。

1
2
3
4
5
6
7
8
sum = 0
for i = 1, 100 do
sum = sum + i
end
--
for j = 100, 1, -2 do
print(j)
end

泛型for循环

遍历数组,对其中的每一个元素进行循环内的操作。Lua中使用迭代器函数ipairs来遍历访问数组,详见示例代码:

1
2
days = {"Suanday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}  
for i,v in ipairs(days) do print(v) end

其中i是数组索引值,v是对应索引的数组元素值。

对于非数组形式的table,使用迭代器函数pairs来遍历访问,详见示例代码:

1
2
colors = {red = "FF0000", green = "00FF00", blue = "0000FF"}
for k,v in pairs(colors) do print(v) end

其中其中k是键,v是对应的值。

repeat…until 循环

repeatuntil之间的语句至少会执行一次。

1
2
3
4
5
rest = 10
repeat
print(string.format("%d",rest))
rest = rest - 1
until rest == 0

函数

函数是对语句和表达式进行抽象的主要方法,既可以用来处理一些特殊的工作,也可以用来计算一些值。

函数定义

Lua中的函数定义方式如下:

1
2
3
4
[作用域] function 函数名( 参数1, 参数2, 参数3..., 参数n)
函数体
return 逗号分隔的返回值
end

函数的作用域有局部与全局之分。定义局部函数使用local关键字,定义表内的函数直接将函数指定给表内对应的键。

以下的示例代码定义了一个求两个数中最大值的函数:

1
2
3
4
5
6
7
8
function (num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end

以下的示例代码使用函数递归调用来实现斐波那契数列求和:

1
2
3
4
function fib(n)
if n < 2 then return 1 end
return fib(n - 2) + fib(n - 1)
end

Lua中的函数是“第一类值”,函数可以保存为变量。

1
2
function f(x) return x * x end
f = function (x) return x * x end

高阶函数

1
2
3
4
5
6
7
8
9
10
11
12
13
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end

function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end

myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

输出为

1
2
这是打印函数 -   ##	10	##
这是打印函数 - ## 7 ##

可变参数

函数可以接受可变数量的参数,在函数参数列表中使用三点(),表示函数有可变的参数。Lua将函数的参数放在一个叫arg的表中,#arg可以计算传入参数的个数。
以下示例代码用来计算几个数的平均值:

1
2
3
4
5
6
7
8
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
return result/#arg
end

多返回值

Lua中的函数可以有多个返回值,返回值之间用逗号隔开,如下示例:

1
2
3
function func(a, b, c)
return a+1, b+2, c+3
end

表是Lua中唯一的复合类型,本质是关联数组,可用来构建不同的数据类型,如数组、字典等。table中的键(数组索引)可以使用除nil外任意类型的值。table的大小是不固定的,你可以根据自己需要进行扩容。

构造

表的构造有以下三种形式:

1
2
3
tab1 = {} -- 构造空表
tab2 = {"apple", "pear", "orange", "grape"} -- 只有值,使用默认的索引
tab3 = { ["key1"] = "val1", ["key2"] = "val2", [3] = "val3"} -- 指定键和值

对于上边的第三种方式,当键为字符串时,可以省略方括号和引号,所以下边的代码与上边的第三行等价:

1
tab3 = { key1 = "val1", key2 = "val2", [3] = "val3"} -- 指定键和值

Lua中的全局变量其实是存储在一个名为_G的table中,所以,以下的代码会输出true

1
print(_G['_G'] == _G) --  输出true

Lua中table有以下的方法:

1
2
3
4
5
6
7
8
9
10
11
table.concat (table [, sep [, start [, end]]]) 
-- 将table中的元素拼接成字符串。列出参数table的数组部分从start位置到end位置的所有元素,元素间以指定的分隔符(sep)隔开,返回拼接完成的字符串。

table.insert (table, [pos,] value)
-- 在table的数组部分指定位置(pos)插入值为value的一个元素,如果未指定pos,则插入位置默认为数组部分末尾

table.remove (table [, pos])
-- 返回table数组部分位于pos位置的元素,其后的元素会被前移,如果未指定pos,默认为table长度,即移除最后一个元素

table.sort (table [, comp])
-- 对给定的table进行升序排序。如果指定了comp,comp必须是一个接收两个参数且返回true或false的函数

数组

table中省略键时,变成使用默认数字索引的数组,索引值是从1开始的。

1
2
3
4
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do
print(v[i])
end

metatable

元表(metatable)可以看做表的表,类似 JavaScript中原型(prototype)的概念。使用元表(metatable)可以改变table的行为,table的每个行为都关联了对应的元方法(metamethod)。主要涉及两个方法:

  • setmetatable(table,metatable):对指定table设置元表,如果table的元表中已存在__metatable键,setmetatable则会失败 。为一个表的元表增加__metatable键可以保护和锁定该表中的元方法;

  • getmetatable(table):返回table的元表。

元表能够被用于定义算术运算符和关系运算符的行为。以下是一段示例代码,为表定义了加法运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
f1 = {a = 1, b = 2}
f2 = {a = 2, b = 3}

-- 此时如果计算 s = f1 + f2 会出错

mt = {}
function mt.__add(x, y)
sum = {}
sum.a = x.a + y.a
sum.b = x.b + y.b
return sum
end

setmetatable(f1, mt)
setmetatable(f2, mt)

-- 实际调用 f1 的 metatable 中的 __add(f1, f2)
-- 所以只为 f1 设置元表即可

s = f1 + f2 -- s = {a = 3, b = 5}

-- 此时如果 s2 = s + s 为错,因为s 未定义元表

Lua中的值都具有元