lua中常量的实现及表的深拷贝实现
2013-09-28 23:18
736 查看
废话:好久没在这里写博客了。。。主要原因是我买了个域名hanxi.info并在github上搭建了个人博客。。。
lua中默认是没有c中的const常量的,在csdn上找到了一个使用setmetatable。参考http://blog.csdn.net/xiaodan007/article/details/6668015。主要原理就是重载__index方法(相当于get方法)和__newindex方法(相当于set方法)。但是他实现的是不支持表中有表的情况的。
下面是我修改后的代码:
我也就是添加了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的值。代码就先不写了,晚了洗洗睡吧。下次有时间会贴代码的。。。
网上关于深拷贝表的资料很少。总共在下面几个网站上找到了答案。
http://blog.sina.com.cn/s/blog_49bdd36d0100fdc1.html(集合了带环的table和不带环的table的解决方案) 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)
先给上递归式的代码吧。
为啥带环的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行,这行不是我想要的,我的目的是拷贝出一份临时表,这分临时表要去除常量的特性,所以我修改如下
这样也就不保留常量特性了。
lua中使用局部函数的好处是很多的。look_up就用到了这个好处,如果lua不支持局部函数,那就只能将look_up表当做参数传递进去了。我之前实现了不支持环的版本,如下:
非递归版本太牛逼了,不做介绍了自己想看源码的去看吧
lua中默认是没有c中的const常量的,在csdn上找到了一个使用setmetatable。参考http://blog.csdn.net/xiaodan007/article/details/6668015。主要原理就是重载__index方法(相当于get方法)和__newindex方法(相当于set方法)。但是他实现的是不支持表中有表的情况的。
下面是我修改后的代码:
function newConst( const_table ) --生成常量表功能 function Const( const_table ) local mt = { __index = function (t,k) 6 if type(const_table[k])=="table" then 7 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的值。代码就先不写了,晚了洗洗睡吧。下次有时间会贴代码的。。。
网上关于深拷贝表的资料很少。总共在下面几个网站上找到了答案。
http://blog.sina.com.cn/s/blog_49bdd36d0100fdc1.html(集合了带环的table和不带环的table的解决方案) 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)
先给上递归式的代码吧。
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中const型变量或者常量的实现
- Lua实现深度拷贝(Deep Copy)
- 在lua里实现常量_类_继承_多态
- Lua实现深度拷贝(Deep Copy)
- LUA 中实现table表的深拷贝实例
- lua实现深度拷贝table表
- warensoft .NET快速开发系列--C# 利用FileStream实现 文件拷贝
- quick-cocos2d-x的热更新机制实现<四>update包(lua)(下)
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- C# Lua实现
- 探索Lua5.2内部实现:TString
- C#实现WinForm下DataGridView控件的拷贝和粘贴
- FreeSWITCH 限制音视频会议数量(lua实现)
- 几个典型的内存拷贝及字符串函数实现
- String类的实现与深浅拷贝问题
- OpenResty(Nginx)+Lua+GraphicsMagick实现缩略图功能
- c# 深拷贝各种实现
- [置顶] 实现内存的移动,功能类似于memmove,可以不实现内存重叠拷贝,不使用任何库函数
- 用SWIG实现C++和Lua互相调用实例