兩個函數徹底理解Lua中的閉包

原文鏈接:https://www.cnblogs.com/tongy0/p/7388653.html

如果損害到原作者的利益,請聯繫,立即刪除!!

本文通過兩個函數徹底搞懂Lua中的閉包,相信看完這兩個函數,應該能理解什麼是Lua閉包。廢話不多說,上 code:

--[[***************************************************************************

                             I'm jacc.kim

    CreateDate: 2017-08-18 11:19:20 
    FileName  : closure.lua
    Version   : 1.00
    Author    : jacc.kim
    Summary   : 

***************************************************************************--]]

--[[
 * summary     : 利用閉包實現帶狀態的迭代器
 * param       : collection 集合.
 * return      : !!!重點注意:函數的返回值是重點.返回值是:一!個!函!數!.而該返回的函數是一個閉包.
--]]
local function elementIterator(collection)
    -- nIndex 爲 elementIterator() 函數的一個局部變量.標記着 collection 當前處理到的 collection 的下標位置 .
    -- 這邊爲每個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過)
    local nIndex               = 0;
    -- nCollectionCount 爲 elementIterator() 函數的一個局部變量.記錄着命令 collection 的長度信息
    -- 這邊爲每個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過)
    local nCollectionCount     = #collection;
    
    -- 閉包.
    -- 說明:01.在 Lua 中,函數內部可以定義函數,被定義的內部函數可以訪問到外層函數定義的局部變量(如:上面的 nIndex、nCollectionCount、collection等,注意:
    --     外層函數的參數其實就是個局部變量).
    --     02.並且非常重要的(閉包的特性:)這些外層函數定義的局部變量的狀態會被閉鈕函數保持住.如 nIndex 起初的時候爲 0 值,執行完下面的
    --       nIndex = nIndex + 1; 後, nIndex 的值爲 1,之後,再次調用該閉包函數時, nIndex = nIndex + 1; 後, nIndex 的值就變爲 2.
    --     03.在Lua中,函數是第一類值.因此,函數可以被賦值給一個變量(局部或全局)、可以作爲函數參數傳遞、可以作爲函數的返回值返回.因此,這邊可以 return 該閉包函數.
    return function ()
        -- nIndex 爲 外層函數的一個局部變量.但 nIndex 的狀態會被該內部函數(可理解爲閉包函數)記憶保持住.因此,每次調用該閉包函數量.nIndex的值部是不斷累加的.
        -- (有點類似 C++ 的 static 特性(注意:僅僅只是類似,不能等同).如:
        -- static auto nIndex = 0;
        -- nIndex = nIndex + 1; // 或 ++nIndex
        -- )
        nIndex                 = nIndex + 1;    -- feature of Colsure
        -- nCollectionCount 爲外層函數的一個局部變量.同樣的該變量的狀態也會被保持住.但在該閉包函數中,沒有去改變 nCollectionCount 的值.因此,該值就一
        -- 值爲集合 collection 的長度.
        
        --[[
        -- 現在重點分析一下這個閉包函數.在此之前,先再介紹另外一個Lua的特性.在Lua中,一個函數可以沒有返回值、可以只返回一個值、也可以返回多個值.重點就是要看上下文本
        -- 到底需要一個函數返回多少個值出去.Ok,這點說明暫時先到此爲止.下面先分析一下該閉包函數.當調用該閉包函數時,
           (!!!這點很重要啊)如果下面的 if 語句條件成立,則該函數會有一個返回值,返回外層函數的參數(collection)的對應索引位置處的元素.
           (!!!這點更加重要)如果下面的 if 語句條件不成立,則該閉包函數是沒有返回值,或者根據上下文,可以返回多個的 nil 值.
        --]]
        if (nIndex <= nCollectionCount) then
            return collection[nIndex];
        end
    end
end

--[[
 * summary    : 測試上面設計的利用閉包實現的迭代器
 * param      : collection 待測試的集合.
 * !!!note    : 01.此處的目的:通過該函數,我們徹底理解閉包在此處起到的作用.
--]]
local function traversalCollection()
    -- collection 爲測試數據.
    local collection           = { "apple", "pear", "orange" };
    -- 這是 Lua 的一個泛型 for 循環.完了完了,沒辦法理解該for循環?不怕,下面我們來徹底搞懂它(注意:搞懂該for循環,我們也就徹底理解什麼是閉包).請看下面的註釋.
    --[[
    for 循環的定義,其實可以理解爲:
    for (條件)
    do
        // some code here........
    end
    如果for的條件成立,則會執行for後面的語句塊 do ... end 中的語句.否則for循環結束.
    此處將下面for循環改寫成另外一種"等價"的寫法
    local iter = elementIterator(collection);
    for element = iter() do
        print(element);
    end
    或者乾脆改寫成 while 語句更容易理解
    local iter = elementIterator(collection);        -- elementIterator()函數其實就好比一個工廠一樣,調用一次,產生一個閉包"實例"
    local element = iter();                          -- 注意:此處 iter() 就是在調用閉兇函數
    while element do
        print(element);
        element = iter();                            -- 注意:此處 iter() 就是在調用閉兇函數
    end
    前文已在 elementIterator() 中的閉包函數中詳細說明了閉包函數的特性,閉包函數會保持外層函數的狀態信息.因此,調用 iter() 函數將返回一個 collection 集合的
    元素,再調用一次將返回下一個元素,一直調用,最後因元素遍歷結束,所以最終會返回一個nil被 element 接收.此時 while 循環結束(注意:Lua中 nil 與 false 都認爲是假).
    至此,相當閉包大家也都理解了.
    --]]
    for element in elementIterator(collection) do
        print(element);
    end
end

traversalCollection();

--[[
上文比較神奇的地方有兩處:
一爲 local iter = elementIterator(collection); 中的 elementIterator(collection)
解釋:elementIterator(collection) 有點類似一個工廠.調用一次則會 產生一個閉包"實例"(該實例"地址"被保存到 iter 變量).而該閉包"實例"擁有
 nIndex、nCollectionCount、collection "成員"變量.這些變量是它的自己的狀態屬性.
如果此時再調用一次 elementIterator(collection) 則又會產生一個新的閉包"實例".每個閉包實例都將有自己的一份狀態屬性.這些狀態屬性的初始化,都是
在閉包函數所在的外層函數處進行初始化(具體可見前文註釋).

二爲  element = iter(); 中的 iter()
解釋:iter() 簡單一點理解,其實就是在調用一個函數而已,僅此而已.然後這個函數返回一個值.只是這個函數比較特別,它有點像c++中的類一樣,有自己的狀態屬性,調用
時,它會根據自己所記錄的狀態屬性數據,進行相應邏輯處理,並返回對應的值.

總結:
    Lua中的閉包,說它是一個(類似c++中的)類,它不是;
    Lua中的閉包,說它是一個函數,它也不完全算是.只是它有點像c++中的函數,並且函數中有一些靜態局部變量一樣.但這樣理解也不完全對.因爲c++中的靜態局部變量
    在整個程序生命週期內,僅一份實例(處於全局靜態存儲區中);
    Lua中的閉包,其實是像c++中的lambda的,但它們也不能完全等價;
    
    那麼Lua中的閉包到底像什麼?個人認爲它與c++中的仿函數是一樣的.因爲它們使用上都像函數,但又都可以持有自己的狀態屬性數據.而且又都可以產生任意多個的"實例"。
--]]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章