国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

Lua中的閉合函數、非全局函數與函數的尾調用詳解

2024-07-21 23:04:00
字體:
來源:轉載
供稿:網友

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

1.閉合函數(closure)

理論上來說,Lua的所有函數都應該稱之為閉合函數,但是,這種反人類的做法,我們還是拋棄吧~

按書上的描述,一個閉合函數就是:一個函數加上該函數所需訪問的所有”非局部的變量“。

理論什么的,很煩人,來看看一個函數:

復制代碼 代碼如下:

function count()
    local i = 0;
    return function()
              i = i + 1;
              return i;
           end
end

 

這個count函數會返回另外一個函數,重點是,這個返回的函數會使用count函數的局部變量。

先來運行,看看效果,使用如下方式調用:

復制代碼 代碼如下:

    local func = count();
    print(func());
    print(func());
    print(func());

 

輸出結果如下:

復制代碼 代碼如下:

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

怎么旁白不出來咆哮一下?(小若:…咳咳…為毛線會這樣?!不應該全部都輸出1嗎?!)
 
在這里,local i就屬于一個非局部變量,因為它既不是全局變量,也不是單純的局部變量(因為另外一個函數可以反問到它)。

 

再來回到定義,count函數里的那個函數,加上非局部變量i,就構成了一個閉合函數了,就這么簡單。
 
對于閉合函數而已,屬于它的非局部變量,并不是在調用它的時候臨時產生的,而是和它一起存在的。

所以每次調用閉合函數,非局部變量的值都不會被重置。
 
如果大家還是不太清楚,那么,我們給這個閉合函數添加一個局部變量吧,修改count函數如下:

復制代碼 代碼如下:

function count()
    return function()
              local i = 999;
              i = i + 1;
              return i;
           end
end

 

這次,把i作為這個內部函數的局部變量了,它不再是“非局部變量”。
仍然像這樣調用:

復制代碼 代碼如下:

    local func = count();
    print(func());
    print(func());
    print(func());

 

輸出結果如下:

復制代碼 代碼如下:

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

陷入, 每一次i變量的值都是全新的。
閉合函數的用處可大著了,我們在開發過程中使用的頻率應該還算比較大的~

 

2.非全局函數

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

來看看這樣的一個函數:

復制代碼 代碼如下:

local function mutou()
  
end

 

這就是一個非全局函數,但,這么簡單的東西我可不會拿出來說~
 
這里我想介紹一個“語法糖”,上面的mutou函數,其實相當于以下的代碼:

復制代碼 代碼如下:

local mutou;
mutou = function ()
  
end

 

這就是我們函數的真實面貌,函數名稱其實也是一個變量名而已。
所以,有時候,我們在定義函數的時候,要注意一下順序。比如,這樣的兩個函數:

復制代碼 代碼如下:

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

 

mutou函數里要調用pangbai函數。
在編譯的時候,mutou函數是編譯不過的,因為這個時候pangbai函數未定義,換句話說,pangbai變量并不存在。
只要換一個方式就可以解決這個問題了:

復制代碼 代碼如下:

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

 

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

3.尾調用

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

尾調用的大致意思是:一個函數的調用是另一個函數的最后一個動作時,這個調用就稱之為尾調用。

比如這樣的函數:

復制代碼 代碼如下:

function mutou()
    return count();
end

 

當mutou函數調用完count函數之后,就沒有其他事情要做了,所以,調用count函數,就屬于尾調用。
但,如果是這樣的函數:

復制代碼 代碼如下:

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

 

這就不屬于尾調用,因為調用完count函數之后,還要取得count的返回值,然后進行一次加法操作,這就不符合定義了。
 
尾調用有什么意義呢?
進行尾調用時不會耗費多余的棧空間,比如這樣一個經典的函數:

復制代碼 代碼如下:

function foo(n)
    if n > 0 then
        return foo(n - 1);
    else
        return "end";
    end
end

 

我們可以試試這樣調用:

復制代碼 代碼如下:

print(foo(99999));

 
結果就是不出所料地輸出了end字符串。
當n > 0時,函數就直接返回foo(n – 1),接著就沒有后續的動作了,所以這符合尾調用的定義。
因此,這個函數的調用不會引起棧溢出。
 
但,如果稍微改改,變成這樣:
復制代碼 代碼如下:

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的函數,應該都介紹完了,我也是按著書的順序在過一遍基礎,然后把覺得有意思的部分用文章記錄下來。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 昌邑市| 乌拉特中旗| 黎城县| 阿瓦提县| 内丘县| 平顺县| 丹凤县| 宣恩县| 石楼县| 博客| 沙湾县| 乌鲁木齐县| 苍南县| 泉州市| 汶上县| 汤阴县| 永顺县| 威海市| 丹东市| 连云港市| 舞钢市| 中卫市| 安龙县| 德州市| 中阳县| 嘉祥县| 永清县| 旌德县| 靖西县| 晋城| 哈巴河县| 永靖县| 永宁县| 德惠市| 衢州市| 聊城市| 纳雍县| 乌苏市| 阿鲁科尔沁旗| 方山县| 新河县|