Lua基本数据类型
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
print(type("Hello world")) -- string
print(type(10.4*3)) -- number
print(type(print)) -- function
print(type(type)) -- function
print(type(true)) -- boolean
print(type(nil)) -- nil
print(type(type(X))) -- string
Lua赋值
local a,b,c = 0, "abc", true
print(a,b,c) --输出: 0 abc true 结论1
A,B,C = 1, "ABC", false
print(A,B,C) --输出: 1 ABC false 结论1
local x,y = 0 --0 nil 结论2
X,Y = 0 --0 nil 结论2
local m,n = 0,1,2
M, N = 0,1,2
print(m,n) --输出:0 1 结论3
print(M,N) --输出:0 1 结论3
local m, n = 1, 2
print(m, n) --1 2 结论4
m, n = n, m
print(m, n) --2 1 结论4
局部变量和全局变量都可以多变量同时赋值
变量的个数多于值的个数时,按照变量个数补
nil
变量的个数少于值的个数时,会忽略多于的值
执行赋值计算时,会优先进行右边的数值,再进行赋值操作,可进行快速交换两个变量的值
特殊全局变量_G
_G
是Lua内置的全局变量,其类型是一个表table
,用于保存所有其他的全局变量。类似于JS中的window
对象。
Lua运算符
算术运算符
操作符 | 描述 | 实例 |
---|---|---|
+ | 加法 | A + B 输出结果 30 |
- | 减法 | A - B 输出结果 -10 |
* | 乘法 | A * B 输出结果 200 |
/ | 除法 | B / A 输出结果 2 |
% | 取余 | B % A 输出结果 0 |
^ | 乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果 -10 |
关系运算符
操作符 | 描述 | 实例 |
---|---|---|
== | 等于,检测两个值是否相等,相等返回 true,否则返回 false | (A == B) 为 false。 |
~= | 不等于,检测两个值是否相等,不相等返回 true,否则返回 false | (A ~= B) 为 true。 |
> | 大于,如果左边的值大于右边的值,返回 true,否则返回 false | (A > B) 为 false。 |
< | 小于,如果左边的值大于右边的值,返回 false,否则返回 true | (A < B) 为 true。 |
>= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false | (A >= B) 返回 false。 |
<= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false | (A <= B) 返回 true。 |
逻辑运算符
操作符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
其他运算符
操作符 | 描述 | 实例 |
---|---|---|
.. | 连接两个字符串 | a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。 |
# | 一元运算符,返回字符串或表的长度。 | #"Hello" 返回 5 |
运算符优先级
^
not - (unary)
* / %
+ -
..
< > <= >= ~= ==
and
or
PS:除了 ^ 和 .. 外所有的二元运算符都是左连接的。
Lua分支
--[[if]]
if 布尔表达式 then
--true时触发
end
--[[if...else]]
if 布尔表达式 then
--true时触发
else
--false时触发
end
--[[if..elseif...else]]
if 布尔表达式 then
--true时触发
elseif
--true时触发
else
--false时触发
end
PS:lua的条件判断和JS不同的是,当条件判断为0时,例if a then
此时a=0
,当前的判断为true类型
Lua中没有switch,也没有三名运算符
Lua循环
local a,b=0,0
--while循环
while a<10 do
a=1+a
print("a="..a)
end
--repeat...until
repeat
b=b+2
print("b="..b)
until b>10
--for
for i=0,3,1 do
print("这是第"..(i+1).."次for循环")
end
Lua函数
function foo(a) print("这是foo函数,参数为" .. a) end
foo(123)
function foo1(a)
a = a + 14
return (a)
end
print(foo1(16))
function foo2()
local a, b, c = 1, "b", false
return a, b, c
end
a, b, c, d = foo2()
print(a, b, c, d)
PS:多返回值时,接收函数返回值的语句类似多变量赋值。如果变量多于函数返回值的数目,则用nil补全;如果变量少于函数返回值的数目,则忽略剩余返回值。
Lua表
数组
array = {"lua", "Tuo"}
for i = 0, 2 do print(array[i]) end--nil lua Tuo
local arr1 = {1, 2, 3, nil, 5}
for k, v in ipairs(arr1) do print(k, v) end--1 1 2 2 3 3
for i, v in pairs(arr1) do print(i, v) end--1 1 2 2 3 3 5 5
local arr = {{1, 2}, {2, 3}}
for i = 1, 2 do
for j = 1, 2 do
print(arr[i][j])
end
end
- lua数组索引值从1开始
- ipairs遍历时只会遍历到nil之前的元素,而使用pairs遍历就会跳过nil元素,继续遍历后面的元素
元组
arr={1,"lua",nil,{1,2}}
for k,v in pairs(arr) do print(k,v) end
元组和数组的差别就是,元组表内的元素的类型不尽相同。
元组声明和赋值的方法,以及遍历的方法和数组一样,这里不再赘述
字典
local obj = {x = 1, y = 2, z = 3}
for k, v in pairs(obj) do print(k, v) end --x 1 z 3 y 2
for k, v in ipairs(obj) do print(k, v) end --
PS:pairs
遍历的顺序没法确定,即不是赋值的顺序,也不是键的自然排序顺序。iparis
无法遍历到字符串索引。
local obj2 = {}
obj2.x = 1
obj2["y"] = 2
obj2.z = 3
可以通过[]
、.
来访问和赋值字典属性
元表
__index
是元表上的一个属性,这个属性可以指向一个表,也可以指向一个函数。它的作用是,当通过键来访问table的时候,如果在table上找不到键对应的值,就会查找table的元表上的__index
指向的表上是否存在键对应的值,如果__index
指向一个函数,这是调用这个函数。
local obj={}
local myMetatable = {name="metatable"}
setmetatable(obj,myMetatable)
print(obj.name)--nil,obj上没有name对应的值
myMetatable.__index =myMetatable;
print(obj.name)--metatable,obj元素的__index指向了myMetatable,而myMetatable上name有对应的值
PS:这里myMetatable.__index
可以指向任意对象,并非一定要指向自身。但是如果指向obj,则会形成死循环报错。
local obj = {}
local myMetatable = {name = "metatable"}
setmetatable(obj, myMetatable)
print(obj.name)--nil
myMetatable.__index = function(obj, key)
print(obj, key)--obj内存地址 键名
if key == "name" then return "__index is a function" end
end
print(obj)--内存地址
print(obj.name)-- __index is a function
print(rawget(obj, "name")) --nil
通过rawget
方法去访问目标表上的属性时,就会忽略目标表的元表的__index属性的,只从目标表上查找属性。
__newindex
是元表上的一个属性,指向一个表。它的作用是,当通过键值向table添加一个属性时,属性实际会添加到__newindex
指向的这个表上。
local obj = {}
local myMetatable = {}
setmetatable(obj, myMetatable)
obj.name = "add name"
print(obj.name)--add name
myMetatable.__newindex = myMetatable
obj.id = "add id"
print(obj.id)--nil
print(myMetatable.id)--add id
上例中,myMetatable
是obj
的元表。
当__newindex
没有指向时,向obj
添加name
属性时,就直接是添加到了obj
上,因此obj.name
是 “add name”;
当__newindex
指向myMetatable
这个表时,向obj
添加id
属性时,就是添加到了myMetatable
上,因此obj.id
是 nil,而myMetatable.id
是 “add id”。
local obj = {}
local myMetatable = {}
setmetatable(obj, myMetatable)
myMetatable.__newindex = function(obj, key, value) print(obj, key, value) end
obj.name = "a"--table: 0000000002572810 name a
obj.name = "b"--table: 0000000002572810 name b
obj.name = "c"--table: 0000000002572810 name c
每次给name
属性赋值,就会调用一次元表的__newindex
方法。这是由于每次给obj
增加属性,属性实际并没有添加到obj
上
如果给__newindex
方法里添加一行rawset
方法,那么就只会打印第一行了,即__newindex
方法只调用一次。
myMetatable.__newindex = function(obj, key, value)
rawset(obj, key, value)
print(obj, key, value)
end
rawset
方法的作用就是,忽略目标表的元表上的__newindex
属性,直接给目标表添加属性和值。
和rawset
对应的还有一个rawget
,他的左右就是忽略目标表的元表上的__index
属性,直接从目标表上查找属性并返回
__tostring
指向一个函数,返回一个字符串。用于处理表如何转换为字符串
local obj = {}
local myMetatable = { }
print(obj) --输出结果:obj的内存地址
setmetatable(obj, myMetatable)
myMetatable.__tostring = function(obj)
return "print obj"
end
print(obj) --输出结果:print obj
__call
指向一个函数,当将table像函数一样调用时,就会调用此方法
local obj = {}
local myMetatable = { }
print(obj) --输出结果: obj的内存地址
setmetatable(obj, myMetatable)
myMetatable.__call = function(obj, args1, args2)
print("__call invoked, args: ",obj, args1, args2)
end
obj()--__call invoked,args: table: 00000000025E58F0 nil nil
obj(1) --__call invoked,args: table: 00000000025E58F0 1 nil
obj("ab", true)--__call invoked,args: table: 00000000025E58F0 ab true
上例中,把obj
当成函数一样调用,并且可以传入任意的参数。__call
函数的第一个参数总是obj
自身,后续的参数则是调用时传入的。
__call
也可以指向带返回值的函数
元表自定义运算符
模式 | 描述 |
---|---|
__add | 对应的运算符 '+'. |
__sub | 对应的运算符 '-'. |
__mul | 对应的运算符 '*'. |
__div | 对应的运算符 '/'. |
__mod | 对应的运算符 '%'. |
__unm | 对应的运算符 '-'. |
__concat | 对应的运算符 '..'. |
__eq | 对应的运算符 '=='. |
__lt | 对应的运算符 '<'. |
__le | 对应的运算符 '<='. |
local obj = {}
local myMetatable = { }
setmetatable(obj, myMetatable)
myMetatable.__mul = function(left,right)
return 100
end
myMetatable.__unm = function(left,right)
return -0.5
end
myMetatable.__concat = function(left,right)
return "-left.right-"
end
myMetatable.__eq = function(left,right)
return true
end
myMetatable.__lt = function(left,right)
return true
end
myMetatable.__le = function(left,right)
return true
end
local obj2 = {};
setmetatable(obj2, myMetatable)
print(obj * obj2) --输出 100
print(-obj) --输出 -0.5
print(obj .. obj2) --输出 -left.right-
print(obj == obj2) --输出 true
print(obj > obj2) --输出 true
print(obj >= obj2) --输出 true
Lua对象
静态类
Person = {}
Person.id = 1
Person.name = "John"
Person.Greet = function() print("Hello") end
Person.Greet2 = function(caller) print("Hello! I`m " .. caller.name) end
function Person.Greet21(caller) print("Hello! I`m " .. caller.name) end
function Person:Greet3() print("Hello! I`m " .. self.name) end
Person.Greet()--Hello
Person.Greet2(Person)--Hello! I`m John
Person.Greet21(Person)--Hello! I`m John
Person:Greet3()--Hello! I`m John
类可以通过表来实现,可以通过属性赋值的方式给类添加成员变量和成员方法
成员方法的定义有2中3种方法:
类名.方法名 = function(args) end
function 类名.方法名 (args) end
-
function 类名:方法名 () end
其中第三种最为重要,在这种定义中,函数体内有一个self属性指向的是调用者本身。其他形式的定义中没有self属性。
普通类和实例化
通过自定义的new方法可以实现构造函数的效果
下面的new函数实现构造函数的步骤是:
声明初始化一个空表obj
将self(这里的self指向的就是调用者也就是类)作为元表,指定给这个obj
将self的__index指向self自身,这样obj就拥有了类上的属性和方法
Person = {}
Person.name = "John Wick"
function Person:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
function Person:Greet() print("Hello ! I`m " .. self.name) end
local p1 = Person:new()
p1:Greet()--Hello ! I`m John Wick
local p2 = Person:new()
p2.name = "Dog"
p2:Greet()--Hello ! I`m Dog
local p3 = Person:new()
p3.name = "Gun"
p3:Greet()--Hello ! I`m Gun
Person:Greet()--Hello ! I`m John Wick
继承和多态
在上述示例中,增加subClass
方法,即可实现继承
实现继承的步骤:
将子类注册到
_G
(全局变量)上保存子类为局部变量
将
self
(也就是父类)作为子类的元表将
self
的__index
指向self
自己。此时子类拥有的父类的方法和属性将子类的
super
指向self
(也就是父类)。此时子类可以通过super
属性访问到父类自己的方法(子类重写父类方法时,子类用来调用父类的方法)
function Person:subClass(className)
_G[className]={}
local class = _G[className]
setmetatable(class,self)
self.__index=self
class.super=self
end
Person:subClass("Killer")
function Killer:Killing()
print(self.name.." is performing mission")
end
function Killer:Greet()
print("Killer Greet")
self.super:Greet(self)
end
local p1=Killer:new()
p1:Greet()--Hello ! I`m John Wick
p1:Killing()--John Wick is performing mission
内置库
lua内置库提供了一些常用的处理方法,这里罗列的一部分,具体的方法自行查阅。
库名 | 功能 | 名称 |
---|---|---|
string | 字符串的处理 | len,sub,gsub,upper,lower,find,match,format,dump… |
table | 表处理 | setn,getn,insert,remove,sort,concat… |
os | 系统相关的处理 | time,date… |
math | 数学方法 | max,min,cos,sin,tan, pow, log, ceil, floor, random… |
io | 处理文件的读写 |