lua中常量的實現及表的深拷貝實現

廢話:很久沒在這裏寫博客了。。。主要緣由是我買了個域名hanxi.info並在github上搭建了我的博客。。。html

 

  lua中默認是沒有c中的const常量的,在csdn上找到了一個使用setmetatable。參考http://blog.csdn.net/xiaodan007/article/details/6668015。主要原理就是重載__index方法(至關於get方法)和__newindex方法(至關於set方法)。可是他實現的是不支持表中有表的狀況的。git

下面是我修改後的代碼:github

 1 function newConst( const_table )    --生成常量表功能
 2     function Const( const_table )
 3         local mt =
 4         {
 5             __index = function (t,k)
 6                 if type(const_table[k])=="table" then
 7                     const_table[k] = newConst(const_table[k])
 8                 end
 9                 return const_table[k]
10             end,
11             __newindex = function (t,k,v)
12                 print("*can't update " .. tostring(const_table) .."[" .. tostring(k) .."] = " .. tostring(v))
13             end
14         }
15         return mt
16     end
17 
18     local t = {}
19     setmetatable(t, Const(const_table))
20     return t
21 end
22 
23 quan = {a = {[1]={2}}}
24 quan.b = quan
25 t = newConst(quan)
26 --t.b = 4
27 print(type(t))
28 print(quan.b)
29 
30 
31 for k,v in pairs(quan) do
32 print(k,v)
33 end

  我也就是添加了6,7,8三行代碼(剛開始想了半天覺得遞歸了,結果思索了下,不是遞歸,只是函數的實現形式,調用newConst的次數就是讀取表的深度,有環的表也不會出現問題的)。__index函數(看參數能夠知道取元素t[k])拿到表的元素,若是元素是表則先將表常量化。__newindex函數(看參數能夠知道寫元素t[k]=v)是給元素賦值,這裏不讓它實現賦值操做,直接打印錯誤提示。web

  爲何要實現這個常量功能,由於如今的手遊項目中使用了lua表存放數值策劃表,每每程序寫代碼時會直接去讀取靜態數據表,萬一不當心把表元素賦值了,那就是把靜態數據改了,會致使遊戲數據錯誤的。實現了這個lua常量就不會出現靜態數據表被修改了。函數

  可是若是須要複製一份靜態數據,而後做爲臨時數據在遊戲邏輯中處理(一個同事就這麼用過。。。),把靜態數據通過了常量處理就不再能被修改了,不常量化也不行,中途被修改了就再也還原不了靜態數據了。所以就須要實現lua表的深拷貝功能了(默然的表與表之間賦值只是簡單的別名而已)。先說下思路吧,實現的效果是:網站

local B = deepcopy(A,n)lua

  把A拷貝給B,n爲拷貝深度。若是n爲nil時那就是說要拷貝到底了,這又出現了中有環的問題了,不考慮環的問題能夠很簡單的遞歸實現,遞歸結束標識就是判斷n的值。代碼就先不寫了,晚了洗洗睡吧。下次有時間會貼代碼的。。。spa

  網上關於深拷貝表的資料不多。總共在下面幾個網站上找到了答案。.net

http://blog.sina.com.cn/s/blog_49bdd36d0100fdc1.html(集合了帶環的table和不帶環的table的解決方案)code

http://www.coronafaqs.com/how-do-i-copy-a-complex-table-in-lua/(就是上面那個帶壞的table的解決方案)

http://lua-users.org/wiki/CopyTable(不帶環的table的解決方案,還有一個淺拷貝實現)

https://gist.github.com/Deco/3985043 (牛逼的解決方案,非遞歸實現,可以處理帶環的table)

  先給上遞歸式的代碼吧。

 1 function table.deepcopy(object)
 2     local lookup_table = {}
 3     local function _copy(object)
 4         if type(object) ~= "table" then
 5             return object
 6         elseif lookup_table[object] then
 7             return lookup_table[object]
 8         end
 9         local new_table = {}
10         lookup_table[object] = new_table
11         for index, value in pairs(object) do
12             new_table[_copy(index)] = _copy(value)
13         end
14         return setmetatable(new_table, getmetatable(object))
15     end
16     return _copy(object)
17 end

爲啥帶環的table用這個函數不會無限遞歸呢?關鍵之處在於lookup_table,它記錄了全部遍歷過的table的副本(新的深拷貝的table),若是出現遍歷過的直接返回那個副本。第12行爲什麼有兩個_copy,這裏用的很巧妙,舉個例子吧。

t = {

    a = 1,

    b = 2,

    c = {

        x = 1,

        y = 2,

        z = 3,

    }

}

t[t.c] = t

t2 = table.deepcopy(t)

print(t2[t2.c])

若是index沒有通過_copy處理,則打印出來的則是nil。爲什麼通過_copy處理必定會是t2.c==t2呢?這也就是第6行判斷的效果了,它返回的index就是t2.c(由於t2.c要麼就是從第7行返回的,要沒是新生成的副本,下次拷貝時仍是取得同一個副本)。

接下來看第14行,這行不是我想要的,個人目的是拷貝出一份臨時表,這分臨時表要去除常量的特性,因此我修改以下

return setmetatable(new_table)

這樣也就不保留常量特性了。

lua中使用局部函數的好處是不少的。look_up就用到了這個好處,若是lua不支持局部函數,那就只能將look_up表當作參數傳遞進去了。我以前實現了不支持環的版本,以下:

 

 1 function table.deepcopy(t, n)
 2     local newT = {}
 3     if n == nil then    -- 默認爲淺拷貝。。。
 4         n = 1
 5     end
 6     for i,v in pairs(t) do
 7         if n>0 and type(v) == "table" then
 8             local T = table.deepcopy(v, n-1)
 9             newT[i] = T
10         else
11             local x = v
12             newT[i] = x
13         end
14     end
15     return newT
16 end

非遞歸版本太牛逼了,不作介紹了本身想看源碼的去看吧

相關文章
相關標籤/搜索