(轉載)【笨木頭Lua專欄】基礎補充03:閉合函數、非全局函數與函數的尾調用

【笨木頭Lua專欄】基礎補充03:閉合函數、非全局函數與函數的尾調用

笨木頭  2014-08-15 21:26    Cocos2d-x Lua    閱讀(4,368)    9條評論

 

上一篇咱們簡單地介紹了Lua的函數,此次,咱們來點特別的,來介紹一下Lua的函數(小若:等等,我是否是錯過了什麼?)函數

 

笨木頭花心貢獻,哈?花心?不,是用心~lua

轉載請註明,原文地址:http://www.benmutou.com/archives/1710spa

文章來源:笨木頭與遊戲開發code

 

1.閉合函數(closure)

理論上來講,Lua的因此函數都應該稱之爲閉合函數,可是,這種反人類的作法,咱們仍是拋棄吧~遊戲

按書上的描述,一個閉合函數就是:一個函數加上該函數所需訪問的全部」非局部的變量「ci

理論什麼的,很煩人,來看看一個函數:遊戲開發

1
2
3
4
5
6
7
function count()
    local i = 0;
    return function()
              i = i + 1;
              return i;
           end
end

這個count函數會返回另一個函數,重點是,這個返回的函數會使用count函數的局部變量。
先來運行,看看效果,使用以下方式調用:開發

1
2
3
4
    local func = count();
    print(func());
    print(func());
    print(func());

輸出結果以下:字符串

[LUA-print] 1
[LUA-print] 2
[LUA-print] 3get

怎麼旁白不出來咆哮一下?(小若:…咳咳…爲毛線會這樣?!不該該所有都輸出1嗎?!)

 

在這裏,local i就屬於一個非局部變量,由於它既不是全局變量,也不是單純的局部變量(由於另一個函數能夠反問到它)。

再來回到定義,count函數裏的那個函數,加上非局部變量i,就構成了一個閉合函數了,就這麼簡單。

 

對於閉合函數而已,屬於它的非局部變量,並非在調用它的時候臨時產生的,而是和它一塊兒存在的。

因此每次調用閉合函數,非局部變量的值都不會被重置。

 

若是你們仍是不太清楚,那麼,咱們給這個閉合函數添加一個局部變量吧,修改count函數以下:

1
2
3
4
5
6
7
function count()
    return function()
              local i = 999;
              i = i + 1;
              return i;
           end
end

此次,把i做爲這個內部函數的局部變量了,它再也不是「非局部變量」。

仍然像這樣調用:

1
2
3
4
    local func = count();
    print(func());
    print(func());
    print(func());

 

輸出結果以下:

[LUA-print] 1000
[LUA-print] 1000
[LUA-print] 1000

陷入, 每一次i變量的值都是全新的。

閉合函數的用處可大着了,咱們在開發過程當中使用的頻率應該還算比較大的~

 

2.非全局函數

又是這種看着就反人類的名詞,非全局,那就是說,不是全局的函數(小若:廢你個話啊!)

來看看這樣的一個函數:

1
2
3
local function mutou()
    
end

這就是一個非全局函數,但,這麼簡單的東西我可不會拿出來講~

 

這裏我想介紹一個「語法糖」,上面的mutou函數,其實至關於如下的代碼:

1
2
3
4
local mutou;
mutou = function ()
    
end

 

這就是咱們函數的真實面貌,函數名稱其實也是一個變量名而已。

因此,有時候,咱們在定義函數的時候,要注意一下順序。好比,這樣的兩個函數:

1
2
3
4
5
6
7
8
local function mutou()
    print("mutou");
    return pangbai();
end

local function pangbai()
    print("pangbai");
end

mutou函數裏要調用pangbai函數。

在編譯的時候,mutou函數是編譯不過的,由於這個時候pangbai函數未定義,換句話說,pangbai變量並不存在。

只要換一個方式就能夠解決這個問題了:

1
2
3
4
5
6
7
8
9
10
11
local mutou;
local pangbai;

mutou = function ()
    print("mutou");
    return pangbai();
end

pangbai = function ()
    print("pangbai");
end

這樣編譯就沒問題了,好吧,也許Lua裏不應叫編譯,反正,不會報語法錯誤了~

 

3.尾調用

好了,一個名詞比一個名詞要反人類。

尾調用的大體意思是:一個函數的調用是另外一個函數的最後一個動做時,這個調用就稱之爲尾調用。

好比這樣的函數:

1
2
3
function mutou()
    return count();
end

當mutou函數調用完count函數以後,就沒有其餘事情要作了,因此,調用count函數,就屬於尾調用。

但,若是是這樣的函數:

1
2
3
function mutou()
    return count() + 1;
end

 

這就不屬於尾調用,由於調用完count函數以後,還要取得count的返回值,而後進行一次加法操做,這就不符合定義了。

 

尾調用有什麼意義呢?

進行尾調用時不會耗費多餘的棧空間,好比這樣一個經典的函數:

1
2
3
4
5
6
7
function foo(n)
    if n > 0 then
        return foo(n - 1);
    else
        return "end";
    end
end

咱們能夠試試這樣調用:

1
print(foo(99999));

 

結果就是不出所料地輸出了end字符串。

當n > 0時,函數就直接返回foo(n – 1),接着就沒有後續的動做了,因此這符合尾調用的定義。

所以,這個函數的調用不會引發棧溢出。

 

但,若是稍微改改,變成這樣:

1
2
3
4
5
6
7
function foo(n)
    if n > 0 then
        return foo(n - 1) + 0;
    else
        return "end";
    end
end

運行的時候,就會報這樣的一個錯誤: [string 「src/main.lua」]:57: stack overflow

 

因爲我用Lua的狀況很少,因此暫時沒有舉個比較實用的例子,但,這天然會是一個很好用的特性。

 

 

好了,關於Lua的函數,應該都介紹完了,我也是按着書的順序在過一遍基礎,而後把以爲有意思的部分用文章記錄下來。

 
原文地址:http://www.benmutou.com/archives/1710
相關文章
相關標籤/搜索