lua中默認是沒有c中的const常量的,在csdn上找到了一個使用setmetatable。主要原理就是重載__index方法(相當於get方法)和__newindex方法(相當於set方法)。但是他實現的是不支持表中有表的情況的。
下面是我修改後的代碼:
function newConst( const_table ) --生成常量表功能
function Const( const_table )
local mt =
{
__index = function (t,k)
if type(const_table[k])=="table" then
const_table[k] = newConst(const_table[k])
end
return const_table[k]
end,
__newindex = function (t,k,v)
print("*can't update " .. tostring(const_table) .."[" .. tostring(k) .."] = " .. tostring(v))
end
}
return mt
end
local t = {}
setmetatable(t, Const(const_table))
return t
end
quan = {a = {[1]={2}}}
quan.b = quan
t = newConst(quan)
--t.b = 4
print(type(t))
print(quan.b)
for k,v in pairs(quan) do
print(k,v)
end
我也就是添加了6,7,8三行代碼(剛開始想了半天以為遞歸了,結果思索了下,不是遞歸,只是函數的實現形式,調用newConst的次數就是讀取表的深度,有環的表也不會出現問題的)。__index函數(看參數可以知道取元素t[k])拿到表的元素,如果元素是表則先將表常量化。__newindex函數(看參數可以知道寫元素t[k]=v)是給元素賦值,這裡不讓它實現賦值操作,直接打印錯誤提示。
為什麼要實現這個常量功能,因為現在的手游項目中使用了lua表存放數值策劃表,往往程序寫代碼時會直接去讀取靜態數據表,萬一不小心把表元素賦值了,那就是把靜態數據改了,會導致游戲數據錯誤的。實現了這個lua常量就不會出現靜態數據表被修改了。
但是如果需要復制一份靜態數據,然後作為臨時數據在游戲邏輯中處理(一個同事就這麼用過。。。),把靜態數據經過了常量處理就再也不能被修改了,不常量化也不行,中途被修改了就再也還原不了靜態數據了。因此就需要實現lua表的深拷貝功能了(默然的表與表之間賦值只是簡單的別名而已)。先說下思路吧,實現的效果是:
local B = deepcopy(A,n)
把A拷貝給B,n為拷貝深度。如果n為nil時那就是說要拷貝到底了,這又出現了中有環的問題了,不考慮環的問題可以很簡單的遞歸實現,遞歸結束標識就是判斷n的值。
先給上遞歸式的代碼吧。
function table.deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
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表當做參數傳遞進去了。我之前實現了不支持環的版本,如下:
function table.deepcopy(t, n)
local newT = {}
if n == nil then -- 默認為淺拷貝。。。
n = 1
end
for i,v in pairs(t) do
if n>0 and type(v) == "table" then
local T = table.deepcopy(v, n-1)
newT[i] = T
else
local x = v
newT[i] = x
end
end
return newT
end
非遞歸版本太牛逼了,不做介紹了自己想看源碼的去看吧。
Lua 的詳細介紹:請點這裡
Lua 的下載地址:請點這裡