03 Lua 数据类型

简介: Lua 体积小、启动速度快,一个完整的 Lua 解释器不过200k,在所有脚本引擎中,Lua 的速度可以说是最快的。所以 Lua 是作为嵌入式脚本的最佳选择。

我是陈皮,一个在互联网 Coding 的 ITer,个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新技术文章。

1 数据类型

  • 字符串(string):双引号或者单引号括起来的字符。
  • 数值类型(number):所有数字,包括十进制,十六进制以及科学计数法等。
  • 布尔类型(boolean):只有真和假两个值,即 true 和 false。
  • 函数(function):可以实现某种功能的代码块。
  • 表(table):lua 语言的核心之一,类似哈希表。
  • 自定义类型(userdata):脚本用户只能使用,不能对其进行定义。
  • 线程(thread):线程类型的值是一个可用于异步计算的协同程序(轻量级有限线程)。
  • 空类型(nil):空的意思,代表什么都没有。

在 Lua 中,可以使用type函数来求出类型。

print(type("chenpi")) -- stringprint(type(123)) -- numberprint(type(true)) -- booleanprint(type(type)) -- functionprint(type({})) -- table

2 字符串(string)

双引号和单引号括起来的字符串使用无差别,都可以对转义字符进行转义。

> print('单引号括起来的字符串\n\r这是换行后内存!')
单引号括起来的字符串
这是换行后内存!
> print("双引号括起来的字符串\n\r这是换行后内存!")
双引号括起来的字符串
这是换行后内存!
> 

字符串还可以使用两个成对的中括号包括字符串,这样存储的是字符串原始值,即使它们是跨行的。


str = [[aabb ccddeeff ff ff]]print(str)
-- 输出结果如下aabbccddeeffffff

3 数值类型(number)

所有数字,包括十进制,十六进制以及科学计数法等。

i = 10j = 10.15k = 0x12h = 1.20e5print(i) -- 10print(j) -- 10.15print(k) -- 18print(h) -- 120000.0

4 布尔类型(boolean)

只有真和假两个值,即 true 和 false。


flag = trueifflagthenprint("flag is true")
elseprint("flag is false")
end-- 输出 flag is true

5 表(table)

表用来存储一些互相关联的元素,每一个元素都有一个关键字和一个值组成,即键值对。表功能类似哈希表。

表中元素的值可以放任何类型的值,例如字符串,数值,表,函数等等。

-- 创建一个空表t = {}
-- 没有显示指定键的值,系统会自动分配键1,2,3...t = {k1 ="v1", "a", "b", "c"}
print(t[0])      -- nilprint(t[1])      -- aprint(t[2])      -- bprint(t[3])      -- cprint(t[4])      -- nilprint(t["k1"])   -- v1

对表中元素的引用,除了使用表名[键名]之外,还可以使用表名.键名,但是后面这种不可以引用键是数值的元素。

t = {k1 ="a", "b", k2 = "c", "d"}
print(t[1])      -- bprint(t[2])      -- dprint(t["k1"])   -- aprint(t.k1)      -- a-- print(t.1)    -- 不合法,会报错t.k1 = "aa"t[1] = "bb"print(t[1])      -- bbprint(t.k1)      -- aa

如果想要删除表中的元素,只需要将键对应的值设置为 nil 即可。

t = {k1 ="a", k2 = "b"}
print(t.k1)      -- at.k1 = nilprint(t.k1)      -- nil

遍历表中的元素,如下:


t = {k1 ="a", "b", k2 = "c", "d"}
fork, vinpairs(t) doprint(k, v)
end-- 输出结果如下1b2dk2ck1a

表中还可以存储函数,以下演示将定义一个函数然后赋值给表中的一个键,最后进行调用。

t = {}
t.func = function()
print("Hello ChenPi!")
endfunctiont.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")     -- 没有指定键,则插入到尾部-- 输出 agbcffori = 1, #tdoprint(t[i])
endtable.remove(t, 2)    -- 删除键为2的元素table.remove(t)       -- 省略键,则删除最后一个元素-- 输出 abcfori = 1, #tdoprint(t[i])
end-- 排序函数table.sort(表名, 排序规则)  -- 排序规则可以省略t = {"d", "a", "e", "c", "h", "g"}
table.sort(t) -- 对值进行排序-- 输出 acdeghfori = 1, #tdoprint(t[i])
endfunctionmysort(a, b)
returna > bendtable.sort(t, mysort)
-- 输出 hgedcafori = 1, #tdoprint(t[i])
end


其实 Lua 中各种函数库(例如 table 库,string 库,math 库等等)就是使用表来存储一系列的函数,所以我们也可以自定义自己的函数库。

-- 定义一个空表chenpifunc = {}
-- 定义第一个函数chenpifunc.func1 = function()
print("Hello ChenPi!")
end-- 定义第二个函数chenpifunc.func2 = function(a, b)
localr = a + breturnrend-- 使用函数库chenpifunc.func1()
print(chenpifunc.func2(5, 8))
-- 输出结果如下HelloChenPi!
13


我们可以借助表来实现数组类型,每一个数组元素底层其实映射着键1,2,3 ... 。


arr = {1, 2, "a", "b", 3, true, 10.51}
-- 打印的数数组的地址print(arr)
-- 遍历数组fork, vinpairs(arr) doprint(k, v)
end-- 打印数组类型print(type(arr))
-- 输出结果如下table: 0x240673011223a4b536true710.51table


5.1 全局表 _G

我们定义的全局变量都存储在全局表_G中。


name = "陈皮"age = 18print(_G["name"])  -- 陈皮print(_G.age)  -- 18t = {name = "Hello Lua"}
func = function()
print("Hi func")
endprint(_G["t"]["name"])  -- Hello Lua_G.func()  -- Hi func

5.2 复制表方式实现面向对象

其实可以通过表来实现面向对象编程,我们知道表可以存储键值对,还可以将一个函数赋值给表中的一个键,所以我们可以将表作为一个类使用。


-- 定义一个表,代表一个类模板Person = {}
Person.name = "ChenPi"Person.eat = function(p)
print(p.name.." is eating")
endprint(Person.name) -- ChenPiPerson.eat(Person) -- ChenPi is eating

我们可以定义一个函数,对上面定义的 Person 表进行克隆它的元素,模拟生成新对象(新表)进行使用。


functionclone(t)
localinst = {}
fork, vinpairs(t) doinst[k] = vendreturninstend-- 定义一个表,代表一个类模板Person = {}
Person.name = "ChenPi"Person.eat = function(p)
print(p.name.." is eating")
end-- 克隆对象localp = clone(Person)
print(p.name) -- Hi ChenPip.eat(p) -- I am eatingp:eat() -- I am eating 等同于调用eat函数,并把自身作为参数传入函数

其实我们可以在表 Person 中定义一个 new 函数,还可以传入参数,从而可以构造指定参数的对象,如下:


functionclone(t)
localinst = {}
fork, vinpairs(t) doinst[k] = vendreturninstend-- 定义一个表,代表一个类模板Person = {}
Person.name = "ChenPi"Person.eat = function(p)
print(p.name.." is eating")
endPerson.new = function(name)
localself = clone(Person)
self.name = namereturnselfend-- 克隆对象localp = Person.new("陈皮")
print(p.name) -- 陈皮p:eat() -- 皮 is eating


我们还可以模拟类继承关系,如下:


functionclone(t)
localinst = {}
fork, vinpairs(t) doinst[k] = vendreturninstend-- 定义一个表,代表一个类模板Person = {}
Person.name = "ChenPi"Person.eat = function(p)
print(p.name.." is eating")
endPerson.new = function(name)
localself = clone(Person)
self.name = namereturnselfend-- 子类定义Student = {age = 18}
Student.study = function()
print("Student is studying...")
endStudent.new = function(name)
localself = Person.new(name)
-- 将子类的元素放入父类中,重复的键还可以达到重写作用fork, vinpairs(Student) doself[k] = vendreturnselfend-- 创建子类对象locals = Student.new("陈皮")
print(s.name) -- 陈皮print(s.age)  -- 18s:eat() -- 陈皮 is eatings.study() -- Student is studying...


5.3 函数闭包方式实现面向对象

我们可以定义一个函数闭包,来创建对象(表)使用,如下所示:

functionPerson(name)
localself = {}
self.name = nameself.eat = function(p)
print(p.name.." is eating")
endreturnselfendlocalp = Person("陈皮")
p:eat() -- 陈皮 is eating


同样,模拟类继承关系,也是可以使用函数闭包的方式,如下所示:


functionPerson(name)
localself = {}
self.name = nameself.eat = function(p)
print(p.name.." is eating")
endreturnselfendlocalp = Person("陈皮")
p:eat() -- 陈皮 is eatingfunctionStudent(name)
localself = Person(name)
self.study = function()
print(self.name.." is studying...")
endreturnselfendlocals = Student("狗蛋")
s:eat()  -- 狗蛋 is eatings.study()  -- 狗蛋 is studying...


6 函数

函数是 function 类型的对象,函数可以被赋值给一个变量,函数中可以返回一个函数,函数可以比较,函数可以作为 table 表中的值。

-- 函数定义1function 函数名(参数)
  函数内容
  ...
end-- 例如functionsum( i, j)
returni + jendprint(sum(10, 23))  -- 函数调用-- 函数定义2函数名 = function(参数)
    函数内容
    ...
end-- 例如sum = function(i, j)
returni + jendprint(sum(10, 23))  -- 函数调用

函数可以不返回值,返回一个值,返回多个值。return 关键字只能在语句块的结尾一句,即 end 之前,else 之前,或者 until 之前。

functionchenpi()
print("no return")  -- no returnendchenpi()
functionchenpi()
return"one return"endprint(chenpi()) -- one returnfunctionchenpi(a, b)
localx = a + blocaly = a - breturnx, yendi, j = chenpi(10, 5)
print(i)  -- 15print(j)  -- 5

如果函数返回值是另一个函数的最后一个参数,或者是多值赋值表达式的最后一个参数,那么函数的所有返回值都能被使用。否则,函数的所有返回值只有第一个返回值才被使用。

functionfunc()
localx, y, z = 1, 2, 3returnx, y, zendprint("Hello", "ChenPi", func())      -- Hello   ChenPi  1       2       3    print("Hello", func(), "ChenPi")      -- Hello   1       ChenPia, b, c, d = 0, func()                -- 0 1 2 3a, b, c, d = func(), 4-- 1 4 nil nil

Lua 中,函数还可以定义可变参数。


function 函数名(...)           -- 三个点代表参数个数不确定function 函数名(a, b, ...)     -- 2个固定参数,其他参数不确定function 函数名(a, ...)        -- 1个固定参数,其他参数不确定

-- 假设定义如下函数functionchenpi(a, b, ...)
print(a)
print(b)
print(arg[1])
print(arg[2])
print("-------")
endchenpi()            -- 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}-- 输出结果如下nilnilnilnil-------1nilnilnil-------12nilnil-------123nil-------1234-------

函数尾调用,当函数的最后返回值是调用另一个函数时,称为尾调函数。Lua 在调用尾调函数时,会先弹出当前函数的栈空间,然后再调用尾调函数,这样能降低函数层层调用过程中的栈消耗,适用于函数递归调用。

-- 以下函数调用耗时比较久functionfunc(i)
whilei < 15doi = i + 1print(i)
func(i)
endreturniendfunc(0)
-- 改变顺序,使用尾调函数,执行速度很快(而且1000还比15次数大很多)functionfunc(i)
ifi > 1000thenreturniendi = i + 1print(i)
func(i)  -- func(i) + 1 不算尾调函数endfunc(0)



本次分享到此结束啦~~

如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!

相关文章
|
6月前
|
消息中间件 Kubernetes NoSQL
Lua基本数据类型
Lua基本数据类型
|
存储 Java C语言
lua变量、数据类型、if判断条件和数据结构table以及【lua 函数】
lua变量、数据类型、if判断条件和数据结构table以及【lua 函数】
99 0
|
XML 存储 Java
【Lua基础 第1章】初识Lua脚本语言、数据类型、全局变量、关键字的使用
初识Lua脚本语言、数据类型、全局变量、关键字的使用
158 0
【Lua基础 第1章】初识Lua脚本语言、数据类型、全局变量、关键字的使用
|
数据采集 存储 大数据
Lua 语法数据类型与变量|学习笔记
快速学习 Lua 语法数据类型与变量
|
6月前
|
存储 NoSQL Redis
Redis的Lua脚本有什么作用?
Redis Lua脚本用于减少网络开销、实现原子操作及扩展指令集。它能合并操作降低网络延迟,保证原子性,替代不支持回滚的事务。通过脚本,代码复用率提高,且可自定义指令,如实现分布式锁,增强Redis功能和灵活性。
251 1
|
5月前
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
228 0
|
1月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
29 2
|
2月前
|
存储 JSON Ubuntu
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
|
3月前
|
存储 NoSQL Redis
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
下一篇
无影云桌面