24、Lua 学习笔记之二(进阶话题)

进阶话题

函数闭包

实例代码

function createCountdownTimer(second)
    local ms = second * 1000  --ms为countDown的Upvalue
    local function countDown()
        ms = ms -1
        return ms
    end

    return countDown
end

local timer1 = createCountdownTimer(1) 

for i = 1, 3 do
    print(timer1()) 
end

输出结果

999
998
997

关于函数闭包描述

  • Upvalue
    一个函数所使用的定义在它的函数体之外的局部变量(external local variable)称为这个函数的upvalue。 在前面的代码中,函数countDown使用的定义在函数createCountdownTimer 中的局部变量ms就是countDown的upvalue,但ms对createCountdownTimer而 言只是一个局部变量,不是upvalue。 Upvalue是Lua不同于C/C++的特有属性,需要结合代码仔细体会。

  • 函数闭包
    一个函数和它所使用的所有upvalue构成了一个函数闭包。

  • Lua函数闭包与C函数的比较
    Lua函数闭包使函数具有保持它自己的状态的能力,从这个意义上说,可以 与带静态局部变量的C函数相类比。但二者有显著的不同:对Lua来说,函数 是一种基本数据类型——代表一种(可执行)对象,可以有自己的状态;但 是对带静态局部变量的C函数来说,它并不是C的一种数据类型,更不会产生 什么对象实例,它只是一个静态地址的符号名称。

基于对象的实现方式

实例代码

local function create(name ,id )
    local data = {name = name ,id = id}  --data为obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue
    local obj = {}  --把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。

    function obj.SetName(name)
        data.name = name 
    end

    function obj.GetName() 
        return data.name
    end

    function obj.SetId(id)
        data.id = id 
    end

    function obj.GetId() 
        return data.id
    end

    return obj
end

输出结果

mycreate’s id:1mycreate’s name:Sam
mycreate’s id:1mycreate’s name:Lucy

有关对象实现的描述

  • 实现方式
    把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。

  • 局限性
    基于对象的实现不涉及继承及多态。但另一方面,脚本编程是否需要继承和多态要视情况而定。

元表

实例代码(1)

local t = {}
local m = {a = "and",b = "Li Lei", c = "Han Meimei"}

setmetatable(t,{__index = m})  --表{ __index=m }作为表t的元表

for k,v in pairs(t) do  --穷举表t
    print("有值吗?")
    print(k,"=>",v)
end

print("-------------")
print(t.b, t.a, t.c)

输出结果

LiLeiandHan Meimei

实例代码(2)

local function add(t1,t2)
    --‘#’运算符取表长度
    assert(#t1 == #t2)
    local length = #t1
    for i = 1,length do
        t1[i] = t1[i] + t2[i]
    end
    return t1
end

--setmetatable返回被设置的表
t1 = setmetatable({1,2,3},{__add = add})
t2 = setmetatable({10,20,30},{__add = add})

for k,v in  pairs(t1) do
    print(k,"=>",v)
end

for k,v in  pairs(t2) do
    print(k,"=>",v)
end

print("---------两元表相加--------------")
t1 = t1 + t2
for i = 1 ,#t1 do
    print(t1[i])
end

输出结果

1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------两元表相加--------------
11
22
33

有关元表的描述

  • 定义
    元表本身只是一个普通的表,通过特定的方法(比如setmetatable)设置到某个对象上,进而影响这个对象的行为;
    一个对象有哪些行为受到元表影响以及这些行为按照何种方式受到影响是受Lua语言约束的。
    比如在前面的代码里,两个表对象的加法运算,如果没有元表的干预,就是一种错误;
    但是Lua规定了元表可以 重载 对象的加法运算符,因此若把定义了加法运算的元表设置到那两个表上,它们就可以做加法了。
    元表是Lua最关键的概念之一,内容也很丰富,请参考Lua文档了解详情。

  • 元表与C++虚表的比较
    如果把表比作对象,元表就是可以改变对象行为的“元”对象。
    在某种程度上,元表可以与C++的虚表做一类比。
    但二者还是迥然不同的,元表可以动态的改变,C++虚表是静态不变的,元表可以影响表(以及其他类型的对象)的很多方面的行为,虚表主要是为了定位对象的虚方法(最多再带上一点点RTTI)。

基于原型的继承

实例代码

local Robot = { name = "Sam", id = 001 }
function Robot:New(extension)
    local t = setmetatable(extension or { }, self) 
    self.__index = self
    return t
end

function Robot:SetName(name)
    self.name = name 
end

function Robot:GetName() 
    return self.name
end

function Robot:SetId(id)
    self.id = id end

function Robot:GetId() 
    return self.id
end

robot = Robot:New()
print("robot's name:", robot:GetName()) 
print("robot's id:", robot:GetId()) 
print("-----------------")

local FootballRobot = Robot:New({position = "right back"})
function FootballRobot:SetPosition(p) 
    self.position = p
end

function FootballRobot:GetPosition()
    return self.position 
end

fr = FootballRobot:New()
print("fr's position:", fr:GetPosition()) 
print("fr's name:", fr:GetName()) 
print("fr's id:", fr:GetId()) 
print("-----------------")

fr:SetName("Bob")
print("fr's name:", fr:GetName()) 
print("robot's name:", robot:GetName())

输出结果

robot’s name:Sam
robot’s id:1

-----------------

fr’s position:right back
fr’s name:Sam
fr’s id:1

-----------------

fr’s name:Bob
robot’s name:Sam

相关描述

prototype模式 一个对象既是一个普通的对象,同时也可以作为创建其他对象的原型的对象(即类对象,class object);动态的改变原型对象的属性就可以动态的影响所有基于此原型的对象;另外,基于一个原型被创建出来的对象可以重载任何属于这个原型对象的方法、属性而不影响原型对象;同时,基于原型被创建出来的对象还可以作为原型来创建其他对象。

实例代码

hello.lua

local pack = require "mypack"  --导入包[注:包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”)]

print(ver or "No ver defined!")
print(pack.ver)

pack.aFunInMyPack()

print(aFunInMyPack or "No aFunInMyPack defined!")
aFuncFromMyPack()

mypack.lua

module(..., package.seeall) --定义包
ver = "0.1 alpha"

function aFunInMyPack() 
    print("Hello!")
end

_G.aFuncFromMyPack = aFunInMyPack

输出结果

Nover defined!
0.1 alpha
Hello!
NoaFunInMyPack defined!
Hello!

有关包的描述

  • 定义
    包是一种组织代码的方式。

  • 实现方式
    一般在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点非常关键。以前面的代码为例, module(…, package.seeall)的意思是定义一个包,包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是mypack,)并且在包的函数环境里可以访问使用包的函数环境(比如,包的实现使用了print,这个变量没有在包里定义,而是定义在使用包的外部环境中)。

  • 使用方式
    一般用require函数来导入一个包,要导入的包必须被置于包路径(packagepath)上。包路径可以通过package.path或者环境变量来设定。一般来说,当前工作路径总是在包路径中。

  • 其他
    请参考Lua手册进一步了解包的详细说明。

参考文献《C/C++程序员的Lua快速入门》

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一名技术极客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值