Lua学习笔记

Lua官网 https://www.lua.org/

在Lua实现带默认参数的函数

在lua之中,它没有提供像C++那样的不同参数列表的同名函数是不同的机制,在它的内部里面,其实现了一种很朴素的做法

假设定义了一个有n个形参的函数 SetArgs(arg1...argn),那么若果开发者在实际调用此函数的时候,填充的参数的个数小于n,那么后面没有添加的参数的实参都默认被设为nil,此时通过or运算符来设定默认值即可;另外,也并不支持 SetArgs(arg1 = 1,arg2 = 2, argn = n )之类的语句,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
myTable = { a= 1, b=2, c=3, d=4 } 

--一种难以言喻的操作
function myTable:SetParam(a1,b1,c1,d1)
    self.a = a1 or "a1"
    self.b = b1 or "b1"
    self.c = c1 or "c1"
    self.d = d1 or "d1"
end

--并没有这种骚操作
--function myTable:SetParam(a1 = "a1",b1 = "b1",c1 = "c1",d1 = "c1")
--    self.a = a1
--    self.b = b1
--    self.c = c1
--    self.d = d1
--end

local temp = myTable
temp:SetParam("Adobe")
print(temp.a)
print(temp.b)
print(temp.c)
print(temp.d)

print("end")

模拟C++的类的继承机制

更多相关资料请看Metatable In Lua 浅尝辄止

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
--lua仿写类似C++的类
--注意,lua的默认参数比较奇葩,在成员函数内部,直接添加xxx1 or xxx2即可
people = { }

function people:new( newName, newIdentity )
    obj = {}
    obj.name = newName or "unknown name1"
    obj.identity = newIdentity or "unknown identity1"
    setmetatable(obj,self)
    self.__index = self
    return obj
end

--添加成员函数
function  people:GetName()
    return self.name
end

function  people:GetIdentity()
    return self.identity
end

function  people:PrintInfo()
    print("object belong to people")
end

--student继承people,此时是相当于将people的元表拷贝到student里面了
student = people:new()

--为了避免类实例在创建的时候修改了类的成员变量,因此采用了临时表obj来作为缓冲
--毕竟Lua本身是没有Lua这个概念的,如果不采用临时Table作为缓冲,是无法把类实例和类之间的联系切断
function student:new( newName, newIdentity, newClassroom, newIndex )
    obj = people:new( newName, newIdentity )
    setmetatable(obj, self)
    --self.__index = self使得自身的属性都能被obj访问到
    self.__index = self

    --添加成员变量
    obj.classroom = newClassroom or "unknown classroom2"
    obj.index = newIndex or "unknown index2"
    return obj
end

--添加成员变量到子类里
function student:GetClassroom()
    return  self.classroom
end

function student:GetIndex()
    return  self.index
end

--重载基类的成员函数
function  student:PrintInfo()
    print("object belong to student")
end

print("startup")
temp = student:new("childname", 56579887, "classroom1st", "1")

--开启了下面语句之后可以发现,此时temp的数值会被temp3所影响
temp3 = student:new("childname3", 4987, "classroom3rd", 10)

baseObj =people:new("basename", 110)

baseObj3 =people:new("basename3", 10000000)

print("---------打印类实例的信息--------")
print("在子类实例中调用基类成员函数")
print(temp:GetIdentity())
print(temp:GetName())

print("在子类实例中调用子类成员函数")
print(temp:GetClassroom())
print(temp:GetIndex())

temp:PrintInfo()

print("调用基类实例的成员函数")
print(baseObj:GetName())
print(baseObj:GetIdentity())

print("---------打印当前定义的基类与子类类型的数据--------")
print("以下为基类的信息")
--而name和name是在执行new函数的时候,才为其类实例追加的成员变量,
--而这些成员变量在原本的people里是没有的,因而为nil,正如people = {}
print(people.name)
print(people.name)
people:PrintInfo()

print("以下为子类类型的信息")
--name和identity是从people继承过来的,并且,在继承的时候,通过new函数临时为其创建的,因此数值有效
print(student.name)
print(student.identity)
--而classroom和index是在执行new函数的时候,才为其类实例追加的成员变量,
--而这些成员变量在原本的student是没有的,因而为nil,正如student = people:new{}
print(student.classroom)
print(student.index)
student:PrintInfo()

print("end")
--注意,在new()内,修改self里面的数值的时候,会导致类的成员变量也同时改变,因为,在Lua里面,本身是没有类这个概念的,我们只可以通过Table来模拟类似的类行为,为了绕过Lua的固有限制,我们在通过new()等函数进行类实例创建的时候,使用临时Table来存放此类的特有的成员变量以及成员变量。
--在上述代码里面,student里的classroom和index是无效数值,都为nil,在student:new()函数体内,使用了临时变量obj来存放其基类的成员变量以及成员函数,通过setmetatable(obj,self)语句,让obj拥有了student里面的成员函数以及基类的特性。而之所以需要执行student = people:new()语句,这是为了可以让student继承people的特性了,否则,所有调用了student类实例所有用到基类的方法都将无效,总而言之,这个metatable的作用有点像C++里面的虚表,专门用来定位子类实例和基类之间的成员函数以及成员变量的地址的

控制Table类型的访问权限

要想实现此效果,需要使用Table里面__index__newindex变量,更多详情请看《用__index和__newindex来限制访问》,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
--访问权限控制函数
function cannotModifyHp(object)
    local proxy = {}
    local mt = {
        __index = object,
    __newindex = function(k,v)
        if k ~= "hp" then
        object[k] = v
        end
    end
    }
    setmetatable(proxy,mt)
    return proxy
end
 
object = {hp = 10,age = 11}
--function object.sethp(self,newhp)
--    self.hp = newhp
--end

function object:sethp(newhp)
    self.hp = newhp
end
 
o = cannotModifyHp(object)
 
o.hp = 100    --修改失效
print(o.hp)
 
o:sethp(111)    --修改失效
print(o.hp)
 
object:sethp(100)    --修改生效
print(o.hp)

Lua上的语法糖收集

v:name(args) 可以被解析成 v.name(v,args) 或者 v.name(self,args)

举个栗子,如下所示:

1
2
3
4
5
6
7
8
object = {hp = 10,age = 11}
function object.SetHp1st(self,newHp)
    self.hp = newHp
end

function object:SetHp2nd(newHp)
    self.hp = newHp
end
  • function funcname() body end 等效于 funcname = function() body end
  • function table.a.b.c.funcname() body end 等效于 table.a.b.c.funcname = function() body end
  • local function funcname() body end 等效于 local funcname; funcname = function() body end 不等效于 local funcname = function() body end

重载运算符Lua的运算符

在Lua之中,我们可以通过Metatable(元表)来重载非数值运算符,能够重载的运算符如下所示:

运算符 在Lua里的关键字 意义
+ add 加法运算
- sub 减法运算
* mul 乘法运算
/ div 除法运算
% mod 取模运算,获取余值
^ pow 幂运算
urm 一元**-**操作符
.. concat 连接操作
# len 取数据类型的长度,比如table长度等
== eq 关系运算符:相等
< lt 关系运算符:小于
<= le 关系运算符:小于或等于
[] index 通过下标访问Table[key],仅用于读取数据
[] newindex 通过下标Table[key] ,仅用于修改数据,如Table[key] = value
未知 call 当Lua调用一个值时调用

以下演示了如何重载支持特定格式的表的运算符操作,如+-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
--定义2个表
--a = {5, 6,10,8,69,10,"hello"}
a = {5, 6}
b = {7, 8}

--用c来做Metatable
c = {}
--重定义加法操作
c.__add = function(op1, op2)
--    将所有元素进行配对相加
    local result = {} 
    local lenght1 = table.maxn(op1)
    local lenght2 = table.maxn(op2)
    if lenght1 == lenght2 then
       for index,item in ipairs(op1) do
           table.insert(result,op1[index]+op2[index])
       end
    end
    return result
--   --枚举op2的所有元素,将其插入到op1之中,相当于C++里面的重载运算符+
--   for key, item in ipairs(op2) do
--      print("key = "..key)
--      print("item = "..item)
--      --将item插入到op1之中
--      table.insert(op1, item)
--   end
--   return op1
end

c.__sub = function (op1,op2)
    local result = {} 
    local lenght1 = table.maxn(op1)
    local lenght2 = table.maxn(op2)
    if lenght1 == lenght2 then
       for index,item in ipairs(op1) do
           table.insert(result,op1[index]-op2[index])
       end
    end
    return result
end

--将a的Metatable设置为c
--将__add的方法应用到类b的Table数据中
--setmetatable(a, c)
setmetatable(b, c)
--d现在的样子是{5,6,7,8}
d = a + b
--d = b + a
for key1,value1 in ipairs(d) do
    print(value1)
end
--print(c.__add(a,b)[1])
--print(c.__add(a,b)[1])
e = a - b
--d = b + a
for key2,value2 in ipairs(e) do
    print(value2)
end

和C++相比,那些常见的知识盲区

  1. Lua没有关系操作符!=,以~=来代替,其它的倒是和C++一样
1
2
3
4
--如果a不为10的话,那么打印a
if a ~= 10 then
  print(a)
end
  1. Lua的函数体不要用{body}来包裹起来,只需要在后面添加上end即可,另外在Lua之中,if代码块不需要用(body)来包裹起来,只需要在判断之中,通过空格区分开来,然后在后面添加then即可,注意,有elseif而没有else if的语句哦,比如if exp then body endelseif exp then body
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function IsMatchedID(oldID, newID)
    if oldID == newID then
        return true
    elseif oldID > newID then
        print("oldID > newID")
        return false
    else
        print("oldID < newID")
        return false
    end
end

print(IsMatchedID(10,13))
print(IsMatchedID(10,10))
print(IsMatchedID(18,10))
  1. 获取Table、字符串等类型的长度,不需要length()等函数来获取,直接用*#*即可
1
2
3
4
tempTable = {897,8,78,7,87,87,8,78,78,7,87,8,7}
print(#tempTable)
tempString = "fadfadfadf"
print(#tempString)

参考资料

0%