Lua闭包使用心得

本站文章均为Jensen抹茶喵原创,转载务必在明显处注明:
转载自【博客园】 原文链接:http://www.cnblogs.com/JensenCat/p/5112420.html

1.什么是闭包

支持闭包特性通常需要一个嵌套函数,通过执行嵌套函数来改变所在父函数的局部变量状态,父函数保存调用上下文状态,而嵌套函数负责修改状态的改变.(简单来说就是得支持函数嵌套)

下面就是一个Lua闭包:

function counter()
    local cnt = 0

    --返回匿名函数也是可以的
    local Closure = function() cnt=cnt+1  print("print:"..cnt)  end
    
    return Closure
end

function main()
  local Closure = counter()
     Closure() --print:1
     Closure() --print:2
     Closure() --print:3
     
     local newCounter = counter()
     newCounter() --print:1
     newCounter() --print:2
     Closure() --print:4
     
     --可以看出他们引用的计数对象是不一样的,更像是同一个类的2个不同的对象
end

 

2.闭包的作用

下面是一个java的类,getName方法获取到了类对象的私有成员变量

class Person
{
  private String name;
  public String getName()
  {
    return name;    
   }    
}

 通过上面的方式可以获取到一个类内部的私有属性,同样的,在lua中可以通过某个方法来获取这个方法的局部变量,然后通过这个方法内的方法来读取想要的变量值。

 function func3()
      local num3 = 44
      function func4()
           return num3
      end

      return func4
end

local func = func3();
print(func())

 

解释:

1.在外部无法获取到func3内部的局部变量,但是func3内部的局部方法func4却可以获取到,因此返回一个func4的引用 ,这样在外部通过这个func4就可以获取到func3的内部变量。

2.虽然是绕了一个圈子,但是在方法外部却通过这样一个手段获取到了内部的值。而这个方法内的局部方法func4就叫做闭包,按照很多书上的概念,这个方法搭建了方法内部与方法外部的桥梁,使得在外部也可以任意的获取到方法内部的资源。

3.但是闭包会造成变量在内存中持久占用,因此会有一定的性能问题,最好不要轻易使用,即便使用也要在恰当的实际进行释放。

 

3.游戏开发中的应用

--以下用cocos2dx中的Lua来举例...
--2dx通过tolua++把类方法导出

--举例api
--按钮响应回调函数格式为:
--luaFunc(event)
--event为触摸按下,触摸移动,触摸离开等事件

--lua中的API为:
--UIButton::addListenHandler(luaFunc)

--实际需求是我按钮按下时,我需要改变按钮自身的纹理...此时回调中却没有按钮本身的对象(sender),怎么办呢?

--利用闭包就轻松解决了

--下面是LUA实战例子:一个testUI的页面类
local testUI = testUI or {}

local testUI:onBtnClick(sender,event)
    --可获取的参数有:隐藏的self,btn,event
end

function testUI:initButton()
     local btn = UIButton:create()

    --重点来了
    btn:addListenHandler(
       function(event)
           --使用闭包把self,btn都传进去了....
           self:onBtnClick(btn,event)
       end
    )
end

return testUI

 

4.lua函数递归以及尾调用消除

1)递归示例:

--反面递归例子(递归必须在初始化以后才能调用)
local func = function(n)
 if n > 0 then return func(n - 1) --此处调用错误
end

--正确例子1
local func
func = function(n)
 if n > 0 then return func(n - 1) --此处调用错误
end

--正确例子2(此处函数展开后解释为例子1的代码再执行)
function func(n)
 if n > 0 then return func(n - 1) --此处调用错误
end

--如果是两个函数嵌套递归(超前递归,必须先声明)
local g
local f

--这里不能加local..不然等于声明了多一个局部变量了,递归的对象就不对了
function g()
  f()
end

--这里不能加local..不然等于声明了多一个局部变量了,递归的对象就不对了
function f()
  g()
end

 

 

2)尾调用消除(递归的时候如果返回的函数是最后的执行...则不损耗栈空间,相当于GOTO语句)

--尾调用消除
function g()
 return a,b
end

--正确例子
function f()
 return g() --正确的尾调用消除
end

--错误例子1
function f(x)
 return g() + 1 --最后执行的是加法
end

--错误例子2
function f(x)
 return (g()) --最后执行的是强制返回1个值
end

--错误例子3
function f(x)
 return x or g() 
end

 总结:由于LUA尾递归调用这个性质,我们可以用GOTO来实现状态机了

posted @ 2016-01-08 10:55  Jensen抹茶喵  阅读(5834)  评论(8编辑  收藏  举报