03 Lua 数据类型
我是陈皮,一个在互联网 Coding 的 ITer,个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新技术文章。
1 数据类型
- 字符串(string):双引号或者单引号括起来的字符。
- 数值类型(number):所有数字,包括十进制,十六进制以及科学计数法等。
- 布尔类型(boolean):只有真和假两个值,即 true 和 false。
- 函数(function):可以实现某种功能的代码块。
- 表(table):lua 语言的核心之一,类似哈希表。
- 自定义类型(userdata):脚本用户只能使用,不能对其进行定义。
- 线程(thread):线程类型的值是一个可用于异步计算的协同程序(轻量级有限线程)。
- 空类型(nil):空的意思,代表什么都没有。
在 Lua 中,可以使用type
函数来求出类型。
print(type("chenpi")) -- string
print(type(123)) -- number
print(type(true)) -- boolean
print(type(type)) -- function
print(type({})) -- table
2 字符串(string)
双引号和单引号括起来的字符串使用无差别,都可以对转义字符进行转义。
> print('单引号括起来的字符串\n\r这是换行后内存!')
单引号括起来的字符串
这是换行后内存!
> print("双引号括起来的字符串\n\r这是换行后内存!")
双引号括起来的字符串
这是换行后内存!
>
字符串还可以使用两个成对的中括号包括字符串,这样存储的是字符串原始值,即使它们是跨行的。
str = [[aa
bb cc
dd
ee
ff ff ff
]]
print(str)
-- 输出结果如下
aa
bb cc
dd
ee
ff ff ff
3 数值类型(number)
所有数字,包括十进制,十六进制以及科学计数法等。
i = 10
j = 10.15
k = 0x12
h = 1.20e5
print(i) -- 10
print(j) -- 10.15
print(k) -- 18
print(h) -- 120000.0
4 布尔类型(boolean)
只有真和假两个值,即 true 和 false。
flag = true
if flag then
print("flag is true")
else
print("flag is false")
end
-- 输出 flag is true
5 表(table)
表用来存储一些互相关联的元素,每一个元素都有一个关键字和一个值组成,即键值对。表功能类似哈希表。
表中元素的值可以放任何类型的值,例如字符串,数值,表,函数等等。
-- 创建一个空表
t = {}
-- 没有显示指定键的值,系统会自动分配键1,2,3...
t = {k1 ="v1", "a", "b", "c"}
print(t[0]) -- nil
print(t[1]) -- a
print(t[2]) -- b
print(t[3]) -- c
print(t[4]) -- nil
print(t["k1"]) -- v1
对表中元素的引用,除了使用表名[键名]
之外,还可以使用表名.键名
,但是后面这种不可以引用键是数值的元素。
t = {k1 ="a", "b", k2 = "c", "d"}
print(t[1]) -- b
print(t[2]) -- d
print(t["k1"]) -- a
print(t.k1) -- a
-- print(t.1) -- 不合法,会报错
t.k1 = "aa"
t[1] = "bb"
print(t[1]) -- bb
print(t.k1) -- aa
如果想要删除表中的元素,只需要将键对应的值设置为 nil 即可。
t = {k1 ="a", k2 = "b"}
print(t.k1) -- a
t.k1 = nil
print(t.k1) -- nil
遍历表中的元素,如下:
t = {k1 ="a", "b", k2 = "c", "d"}
for k, v in pairs(t) do
print(k, v)
end
-- 输出结果如下
1 b
2 d
k2 c
k1 a
表中还可以存储函数,以下演示将定义一个函数然后赋值给表中的一个键,最后进行调用。
t = {}
t.func = function()
print("Hello ChenPi!")
end
function t.func()
print("Hello ChenPi!")
end
--[[
上面和此种方式一样,但是推荐使用上面的方式
function t.func()
print("Hello ChenPi!")
end
--]]
t.func() -- Hello ChenPi!
我们可以借助table
库,使用它定义的函数来操作表。
-- 插入元素,删除元素
table.insert(表名, 键, 值)
table.remove(表名, 键)
t = {"a", "b", "c"}
table.insert(t, 2, "g") -- 在第二个位置插入元素,原来键为2的值依次往后移
table.insert(t, "f") -- 没有指定键,则插入到尾部
-- 输出 agbcf
for i = 1, #t do
print(t[i])
end
table.remove(t, 2) -- 删除键为2的元素
table.remove(t) -- 省略键,则删除最后一个元素
-- 输出 abc
for i = 1, #t do
print(t[i])
end
-- 排序函数
table.sort(表名, 排序规则) -- 排序规则可以省略
t = {"d", "a", "e", "c", "h", "g"}
table.sort(t) -- 对值进行排序
-- 输出 acdegh
for i = 1, #t do
print(t[i])
end
function mysort(a, b)
return a > b
end
table.sort(t, mysort)
-- 输出 hgedca
for i = 1, #t do
print(t[i])
end
其实 Lua 中各种函数库(例如 table 库,string 库,math 库等等)就是使用表来存储一系列的函数,所以我们也可以自定义自己的函数库。
-- 定义一个空表
chenpifunc = {}
-- 定义第一个函数
chenpifunc.func1 = function()
print("Hello ChenPi!")
end
-- 定义第二个函数
chenpifunc.func2 = function(a, b)
local r = a + b
return r
end
-- 使用函数库
chenpifunc.func1()
print(chenpifunc.func2(5, 8))
-- 输出结果如下
Hello ChenPi!
13
我们可以借助表来实现数组类型,每一个数组元素底层其实映射着键1,2,3 … 。
arr = {1, 2, "a", "b", 3, true, 10.51}
-- 打印的数数组的地址
print(arr)
-- 遍历数组
for k, v in pairs(arr) do
print(k, v)
end
-- 打印数组类型
print(type(arr))
-- 输出结果如下
table: 0x2406730
1 1
2 2
3 a
4 b
5 3
6 true
7 10.51
table
5.1 全局表 _G
我们定义的全局变量都存储在全局表_G
中。
name = "陈皮"
age = 18
print(_G["name"]) -- 陈皮
print(_G.age) -- 18
t = {name = "Hello Lua"}
func = function()
print("Hi func")
end
print(_G["t"]["name"]) -- Hello Lua
_G.func() -- Hi func
5.2 复制表方式实现面向对象
其实可以通过表来实现面向对象编程,我们知道表可以存储键值对,还可以将一个函数赋值给表中的一个键,所以我们可以将表作为一个类使用。
-- 定义一个表,代表一个类模板
Person = {}
Person.name = "ChenPi"
Person.eat = function(p)
print(p.name.." is eating")
end
print(Person.name) -- ChenPi
Person.eat(Person) -- ChenPi is eating
我们可以定义一个函数,对上面定义的 Person 表进行克隆它的元素,模拟生成新对象(新表)进行使用。
function clone(t)
local inst = {}
for k, v in pairs(t) do
inst[k] = v
end
return inst
end
-- 定义一个表,代表一个类模板
Person = {}
Person.name = "ChenPi"
Person.eat = function(p)
print(p.name.." is eating")
end
-- 克隆对象
local p = clone(Person)
print(p.name) -- Hi ChenPi
p.eat(p) -- I am eating
p:eat() -- I am eating 等同于调用eat函数,并把自身作为参数传入函数
其实我们可以在表 Person 中定义一个 new 函数,还可以传入参数,从而可以构造指定参数的对象,如下:
function clone(t)
local inst = {}
for k, v in pairs(t) do
inst[k] = v
end
return inst
end
-- 定义一个表,代表一个类模板
Person = {}
Person.name = "ChenPi"
Person.eat = function(p)
print(p.name.." is eating")
end
Person.new = function(name)
local self = clone(Person)
self.name = name
return self
end
-- 克隆对象
local p = Person.new("陈皮")
print(p.name) -- 陈皮
p:eat() -- 皮 is eating
我们还可以模拟类继承关系,如下:
function clone(t)
local inst = {}
for k, v in pairs(t) do
inst[k] = v
end
return inst
end
-- 定义一个表,代表一个类模板
Person = {}
Person.name = "ChenPi"
Person.eat = function(p)
print(p.name.." is eating")
end
Person.new = function(name)
local self = clone(Person)
self.name = name
return self
end
-- 子类定义
Student = {age = 18}
Student.study = function()
print("Student is studying...")
end
Student.new = function(name)
local self = Person.new(name)
-- 将子类的元素放入父类中,重复的键还可以达到重写作用
for k, v in pairs(Student) do
self[k] = v
end
return self
end
-- 创建子类对象
local s = Student.new("陈皮")
print(s.name) -- 陈皮
print(s.age) -- 18
s:eat() -- 陈皮 is eating
s.study() -- Student is studying...
5.3 函数闭包方式实现面向对象
我们可以定义一个函数闭包,来创建对象(表)使用,如下所示:
function Person(name)
local self = {}
self.name = name
self.eat = function(p)
print(p.name.." is eating")
end
return self
end
local p = Person("陈皮")
p:eat() -- 陈皮 is eating
同样,模拟类继承关系,也是可以使用函数闭包的方式,如下所示:
function Person(name)
local self = {}
self.name = name
self.eat = function(p)
print(p.name.." is eating")
end
return self
end
local p = Person("陈皮")
p:eat() -- 陈皮 is eating
function Student(name)
local self = Person(name)
self.study = function()
print(self.name.." is studying...")
end
return self
end
local s = Student("狗蛋")
s:eat() -- 狗蛋 is eating
s.study() -- 狗蛋 is studying...
6 函数
函数是 function 类型的对象,函数可以被赋值给一个变量,函数中可以返回一个函数,函数可以比较,函数可以作为 table 表中的值。
-- 函数定义1
function 函数名(参数)
函数内容
...
end
-- 例如
function sum( i, j)
return i + j
end
print(sum(10, 23)) -- 函数调用
-- 函数定义2
函数名 = function(参数)
函数内容
...
end
-- 例如
sum = function(i, j)
return i + j
end
print(sum(10, 23)) -- 函数调用
函数可以不返回值,返回一个值,返回多个值。return 关键字只能在语句块的结尾一句
,即 end 之前,else 之前,或者 until 之前。
function chenpi()
print("no return") -- no return
end
chenpi()
function chenpi()
return "one return"
end
print(chenpi()) -- one return
function chenpi(a, b)
local x = a + b
local y = a - b
return x, y
end
i, j = chenpi(10, 5)
print(i) -- 15
print(j) -- 5
如果函数返回值是另一个函数的最后一个参数,或者是多值赋值表达式的最后一个参数,那么函数的所有返回值都能被使用。否则,函数的所有返回值只有第一个返回值才被使用。
function func()
local x, y, z = 1, 2, 3
return x, y, z
end
print("Hello", "ChenPi", func()) -- Hello ChenPi 1 2 3
print("Hello", func(), "ChenPi") -- Hello 1 ChenPi
a, b, c, d = 0, func() -- 0 1 2 3
a, b, c, d = func(), 4 -- 1 4 nil nil
Lua 中,函数还可以定义可变参数。
function 函数名(...) -- 三个点代表参数个数不确定
function 函数名(a, b, ...) -- 2个固定参数,其他参数不确定
function 函数名(a, ...) -- 1个固定参数,其他参数不确定
-- 假设定义如下函数
function chenpi(a, b, ...)
print(a)
print(b)
print(arg[1])
print(arg[2])
print("-------")
end
chenpi() -- a=nil,b=nil,arg={}
chenpi(1) -- a=1,b=nil,arg={}
chenpi(1, 2) -- a=1,b=2,arg={}
chenpi(1, 2, 3) -- a=1,b=2,arg={3}
chenpi(1, 2, 3, 4) -- a=1,b=2,arg={3,4}
-- 输出结果如下
nil
nil
nil
nil
-------
1
nil
nil
nil
-------
1
2
nil
nil
-------
1
2
3
nil
-------
1
2
3
4
-------
函数尾调用
,当函数的最后返回值是调用另一个函数时,称为尾调函数
。Lua 在调用尾调函数时,会先弹出当前函数的栈空间,然后再调用尾调函数,这样能降低函数层层调用过程中的栈消耗,适用于函数递归调用。
-- 以下函数调用耗时比较久
function func(i)
while i < 15 do
i = i + 1
print(i)
func(i)
end
return i
end
func(0)
-- 改变顺序,使用尾调函数,执行速度很快(而且1000还比15次数大很多)
function func(i)
if i > 1000 then
return i
end
i = i + 1
print(i)
func(i) -- func(i) + 1 不算尾调函数
end
func(0)
本次分享到此结束啦~~
如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!
- 点赞
- 收藏
- 关注作者
评论(0)