我是陈皮,一个在互联网 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)
本次分享到此结束啦~~
如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!