兩個函數完全理解Lua中的閉包

本文經過兩個函數完全搞懂Lua中的閉包,相信看完這兩個函數,應該能理解什麼是Lua閉包。廢話很少說,上 code:c++

  1 --[[***************************************************************************
  2 
  3                              I'm jacc.kim
  4 
  5     CreateDate: 2017-08-18 11:19:20 
  6     FileName  : closure.lua
  7     Version   : 1.00
  8     Author    : jacc.kim
  9     Summary   : 
 10 
 11 ***************************************************************************--]]
 12 
 13 --[[
 14  * summary     : 利用閉包實現帶狀態的迭代器
 15  * param       : collection 集合.
 16  * return      : !!!重點注意:函數的返回值是重點.返回值是:一!個!函!數!.而該返回的函數是一個閉包.
 17 --]]
 18 local function elementIterator(collection)
 19     -- nIndex 爲 elementIterator() 函數的一個局部變量.標記着 collection 當前處理到的 collection 的下標位置 .
 20     -- 這邊爲每一個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過)
 21     local nIndex               = 0;
 22     -- nCollectionCount 爲 elementIterator() 函數的一個局部變量.記錄着命令 collection 的長度信息
 23     -- 這邊爲每一個閉包"實例"初始化它們的"成員變量"的值.(不理解這句的,可先跳過)
 24     local nCollectionCount     = #collection;
 25     
 26     -- 閉包.
 27     -- 說明:01.在 Lua 中,函數內部能夠定義函數,被定義的內部函數能夠訪問到外層函數定義的局部變量(如:上面的 nIndex、nCollectionCount、collection等,注意:
 28     --     外層函數的參數其實就是個局部變量).
 29     --     02.而且很是重要的(閉包的特性:)這些外層函數定義的局部變量的狀態會被閉鈕函數保持住.如 nIndex 起初的時候爲 0 值,執行完下面的
 30     --       nIndex = nIndex + 1; 後, nIndex 的值爲 1,以後,再次調用該閉包函數時, nIndex = nIndex + 1; 後, nIndex 的值就變爲 2.
 31     --     03.在Lua中,函數是第一類值.所以,函數能夠被賦值給一個變量(局部或全局)、能夠做爲函數參數傳遞、能夠做爲函數的返回值返回.所以,這邊能夠 return 該閉包函數.
 32     return function ()
 33         -- nIndex 爲 外層函數的一個局部變量.但 nIndex 的狀態會被該內部函數(可理解爲閉包函數)記憶保持住.所以,每次調用該閉包函數量.nIndex的值部是不斷累加的.
 34         -- (有點相似 C++ 的 static 特性(注意:僅僅只是相似,不能等同).如:
 35         -- static auto nIndex = 0;
 36         -- nIndex = nIndex + 1; // 或 ++nIndex
 37         -- )
 38         nIndex                 = nIndex + 1;    -- feature of Colsure
 39         -- nCollectionCount 爲外層函數的一個局部變量.一樣的該變量的狀態也會被保持住.但在該閉包函數中,沒有去改變 nCollectionCount 的值.所以,該值就一
 40         -- 值爲集合 collection 的長度.
 41         
 42         --[[
 43         -- 如今重點分析一下這個閉包函數.在此以前,先再介紹另一個Lua的特性.在Lua中,一個函數能夠沒有返回值、能夠只返回一個值、也能夠返回多個值.重點就是要看上下文本
 44         -- 到底須要一個函數返回多少個值出去.Ok,這點說明暫時先到此爲止.下面先分析一下該閉包函數.當調用該閉包函數時,
 45            (!!!這點很重要啊)若是下面的 if 語句條件成立,則該函數會有一個返回值,返回外層函數的參數(collection)的對應索引位置處的元素.
 46            (!!!這點更加劇要)若是下面的 if 語句條件不成立,則該閉包函數是沒有返回值,或者根據上下文,能夠返回多個的 nil 值.
 47         --]]
 48         if (nIndex <= nCollectionCount) then
 49             return collection[nIndex];
 50         end
 51     end
 52 end
 53 
 54 --[[
 55  * summary    : 測試上面設計的利用閉包實現的迭代器
 56  * param      : collection 待測試的集合.
 57  * !!!note    : 01.此處的目的:經過該函數,咱們完全理解閉包在此處起到的做用.
 58 --]]
 59 local function traversalCollection()
 60     -- collection 爲測試數據.
 61     local collection           = { "apple", "pear", "orange" };
 62     -- 這是 Lua 的一個泛型 for 循環.完了完了,沒辦法理解該for循環?不怕,下面咱們來完全搞懂它(注意:搞懂該for循環,咱們也就完全理解什麼是閉包).請看下面的註釋.
 63     --[[
 64     for 循環的定義,其實能夠理解爲:
 65     for (條件)
 66     do
 67         // some code here........
 68     end
 69     若是for的條件成立,則會執行for後面的語句塊 do ... end 中的語句.不然for循環結束.
 70     此處將下面for循環改寫成另一種"等價"的寫法
 71     local iter = elementIterator(collection);
 72     for element = iter() do
 73         print(element);
 74     end
 75     或者乾脆改寫成 while 語句更容易理解
 76     local iter = elementIterator(collection);        -- elementIterator()函數其實就比如一個工廠同樣,調用一次,產生一個閉包"實例"
 77     local element = iter();                          -- 注意:此處 iter() 就是在調用閉兇函數
 78     while element do
 79         print(element);
 80         element = iter();                            -- 注意:此處 iter() 就是在調用閉兇函數
 81     end
 82     前文已在 elementIterator() 中的閉包函數中詳細說明了閉包函數的特性,閉包函數會保持外層函數的狀態信息.所以,調用 iter() 函數將返回一個 collection 集合的
 83     元素,再調用一次將返回下一個元素,一直調用,最後因元素遍歷結束,因此最終會返回一個nil被 element 接收.此時 while 循環結束(注意:Lua中 nil 與 false 都認爲是假).
 84     至此,至關閉包你們也都理解了.
 85     --]]
 86     for element in elementIterator(collection) do
 87         print(element);
 88     end
 89 end
 90 
 91 traversalCollection();
 92 
 93 --[[
 94 上文比較神奇的地方有兩處:
 95 一爲 local iter = elementIterator(collection); 中的 elementIterator(collection)
 96 解釋:elementIterator(collection) 有點相似一個工廠.調用一次則會 產生一個閉包"實例"(該實例"地址"被保存到 iter 變量).而該閉包"實例"擁有
 97  nIndex、nCollectionCount、collection "成員"變量.這些變量是它的本身的狀態屬性.
 98 若是此時再調用一次 elementIterator(collection) 則又會產生一個新的閉包"實例".每一個閉包實例都將有本身的一份狀態屬性.這些狀態屬性的初始化,都是
 99 在閉包函數所在的外層函數處進行初始化(具體可見前文註釋).
100 
101 二爲  element = iter(); 中的 iter()
102 解釋:iter() 簡單一點理解,其實就是在調用一個函數而已,僅此而已.而後這個函數返回一個值.只是這個函數比較特別,它有點像c++中的類同樣,有本身的狀態屬性,調用
103 時,它會根據本身所記錄的狀態屬性數據,進行相應邏輯處理,並返回對應的值.
104 
105 總結:
106     Lua中的閉包,說它是一個(相似c++中的)類,它不是;
107     Lua中的閉包,說它是一個函數,它也不徹底算是.只是它有點像c++中的函數,而且函數中有一些靜態局部變量同樣.但這樣理解也不徹底對.由於c++中的靜態局部變量
108     在整個程序生命週期內,僅一份實例(處於全局靜態存儲區中);
109     Lua中的閉包,實際上是像c++中的lambda的,但它們也不能徹底等價;
110     
111  那麼Lua中的閉包到底像什麼?我的認爲它與c++中的仿函數是同樣的.由於它們使用上都像函數,但又均可以持有本身的狀態屬性數據.並且又均可以產生任意多個的"實例"112 --]]
相關文章
相關標籤/搜索