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來實現狀態機了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM