歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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

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 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved