解决原生pack的nil截断问题
代码语言:javascript复制local unpack = unpack or table.unpack
-- 解决原生pack的nil截断问题,SafePack与SafeUnpack要成对使用
function SafePack(...)
local params = {...}
params.n = select('#', ...) --返回可变参数的数量,赋值给n
return params
end
-- 解决原生unpack的nil截断问题,SafePack与SafeUnpack要成对使用
function SafeUnpack(safe_pack_tb)
return unpack(safe_pack_tb, 1, safe_pack_tb.n)
end
-- 对两个SafePack的表执行连接
function ConcatSafePack(safe_pack_l, safe_pack_r)
local concat = {}
for i = 1,safe_pack_l.n do
concat[i] = safe_pack_l[i]
end
for i = 1,safe_pack_r.n do
concat[safe_pack_l.n i] = safe_pack_r[i]
end
concat.n = safe_pack_l.n safe_pack_r.n
return concat
end
实际操作
代码语言:javascript复制local t = {nil,1,nil,3,nil,4,nil}
print(#t) -->0
print(select('#',nil,1,nil,3,nil,4,nil)) -->7
print(unpack(t)) -->空,因为第一个nil就一已经截断了
function c(a,b,c,d,e)
print(a,b,c,d,e)
end
function a()
local temp = {nil,1,nil,3,nil}
c(unpack(temp))
end
a() -->nil 1 nil nil nil
function b()
local temp =SafePack(nil,1,nil,3,nil)
c(SafeUnpack(temp))
end
b() -->nil 1 nil 3 nil
#table的坑点 如果传递的数组中带有 nil 值空洞,# 操作符返回的数值并不能反映真实的大小。 简单说,Lua 里面 table 的长度的定义跟其他语言的不同。table 的长度,被定义成第一个值为 nil 的整数键(而不是像通常认为那样,等价于元素的数量)。 如果一个 array-like table 里面存在空洞,那么任意 nil 值前面的索引都有可能是 # 操作符返回的值。
闭包绑定
代码语言:javascript复制-- 闭包绑定
function Bind(self, func, ...)
assert(self == nil or type(self) == "table")
assert(func ~= nil and type(func) == "function")
local params = nil
if self == nil then
params = SafePack(...)
else
params = SafePack(self, ...)
end
return function(...)
local args = ConcatSafePack(params, SafePack(...))
func(SafeUnpack(args))
end
end
-- 回调绑定
-- 重载形式:
-- 1、成员函数、私有函数绑定:BindCallback(obj, callback, ...)
-- 2、闭包绑定:BindCallback(callback, ...)
function BindCallback(...)
local bindFunc = nil
local params = SafePack(...)
assert(params.n >= 1, "BindCallback : error params count!")
if type(params[1]) == "table" and type(params[2]) == "function" then
bindFunc = Bind(...)
elseif type(params[1]) == "function" then
bindFunc = Bind(nil, ...)
else
error("BindCallback : error params list!")
end
return bindFunc
end
单元测试 –下面是单元测试
代码语言:javascript复制local function TestBind()
local class = BaseClass("class")
class.callback = function(self, a, b, c, d)
self.var = self.var 1
self.a = a
self.b = b
self.c = c
self.d = d
end
local inst = class.New()
inst.var = 111
inst.a = nil
inst.b = nil
inst.c = nil
inst.d = nil
local bindFunc = Bind(inst, inst.callback, "aaa", 1234)
assert(inst.var == 111)
assert(inst.a == nil)
assert(inst.b == nil)
assert(inst.c == nil)
assert(inst.d == nil)
bindFunc()
assert(inst.var == 112)
assert(inst.a == "aaa")
assert(inst.b == 1234)
assert(inst.c == nil)
assert(inst.d == nil)
bindFunc("rrr")
assert(inst.var == 113)
assert(inst.a == "aaa")
assert(inst.b == 1234)
assert(inst.c == "rrr")
assert(inst.d == nil)
bindFunc("kkk", 999)
assert(inst.var == 114)
assert(inst.a == "aaa")
assert(inst.b == 1234)
assert(inst.c == "kkk")
assert(inst.d == 999)
local inst2 = class.New()
inst2.var = 999
inst2.a = nil
inst2.b = nil
inst2.c = nil
inst2.d = nil
local bindFunc2 = Bind(inst2, inst2.callback, "bbb", 4321)
assert(inst2.var == 999)
assert(inst2.a == nil)
assert(inst2.b == nil)
assert(inst2.c == nil)
assert(inst2.d == nil)
bindFunc2()
assert(inst2.var == 1000)
assert(inst2.a == "bbb")
assert(inst2.b == 4321)
assert(inst2.c == nil)
assert(inst2.d == nil)
bindFunc2("qqq")
bindFunc2("vvv", 765)
assert(inst.var == 114)
assert(inst.a == "aaa")
assert(inst.b == 1234)
assert(inst.c == "kkk")
assert(inst.d == 999)
assert(inst2.var == 1002)
assert(inst2.a == "bbb")
assert(inst2.b == 4321)
assert(inst2.c == "vvv")
assert(inst2.d == 765)
local bindFunc3 = Bind(inst, inst.callback)
bindFunc3()
assert(inst.var == 115)
assert(inst.a == nil)
assert(inst.b == nil)
assert(inst.c == nil)
assert(inst.d == nil)
bindFunc3(4532, "4532")
assert(inst.var == 116)
assert(inst.a == 4532)
assert(inst.b == "4532")
assert(inst.c == nil)
assert(inst.d == nil)
local bindFunc4 = Bind(inst, inst.callback, nil, "ttt")
bindFunc4()
assert(inst.var == 117)
assert(inst.a == nil)
assert(inst.b == "ttt")
assert(inst.c == nil)
assert(inst.d == nil)
bindFunc4(nil, "4532")
assert(inst.var == 118)
assert(inst.a == nil)
assert(inst.b == "ttt")
assert(inst.c == nil)
assert(inst.d == "4532")
end
local function Run()
TestBind()
print("LuaUtilTest Pass!")
end
Run()
如何理解闭包绑定 1.local bindFunc = Bind(inst, inst.callback, “aaa”, 1234) Bind函数内部params = SafePack(self, …):把self,和Bind后面参数组合pack 2.Bind函数内部的return function(…):这里的…跟params = SafePack(self, …)中…不一样,这里是指bindFunc 传递过来的参数 3.整个逻辑:SafeUnpack:self(或者nil),Bind的参数列表,bindFunc的参数列表
深拷贝
代码语言:javascript复制-- 深拷贝对象
function 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 new_table
--return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
单元测试
代码语言:javascript复制tabA = { x= 1}
tabB = tabA
print(tabB.x) --1
tabA.x = 2
print(tabB.x) --2
tabC = DeepCopy(tabA)
print(tabC.x) --2
tabA.x = 3
print(tabC.x) --2
单元测试二
代码语言:javascript复制tabA = { x= 1,inside = { y =1}}
tabB = tabA
print(tabB.inside.y) --1
tabA.inside.y = 2
print(tabB.inside.y) --2
tabC = DeepCopy(tabA)
print(tabC.inside.y)--2
tabA.inside.y = 3
print(tabC.inside.y) --2
代码解析
- tabB = tabA ,相当于是对象起别名,或者说赋值指针,tabA的任何改动也会实装在tabB上
- lookup_table相当于一个记忆表,里面的key为table的地址,这样可以保证每一个key都是唯一的,里面只包含,一个是要深拷贝的tabA,另外是tabA的里面的域为table 的
- _copy里面执行逻辑,如果复制的是值,直接返回,如果复制的是表,在记忆表里找,没找到接着创建一个记忆表key 为inside,然后执行复制值时,又创建了一个记忆表
可以做此测试加强代码回调调用理解 –深拷贝
代码语言:javascript复制function DeepCopy(object)
local lookup_table = {}
local i = 0
local function _copy(object)
print("copy")
print(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
i = i 1
print("index:" .. i)
for index, value in pairs(lookup_table) do
print(value)
end
return new_table
--return setmetatable(new_table, {__index = object})
end
return _copy(object)
end
--深拷贝测试
tabA = { 2,x= 1,inside = { y =2}}
tabB = tabA
print(tabA)
print(tabA.inside)
tabC = DeepCopy(tabA)
--输出
table: 00E87FA8 --a地址
table: 00E882C8 --a.inside地址
copy
table: 00E87FA8 --copy a 记忆表找不到a ,开始copy a 的index 和值
copy
1 -- copy 索引1 ,立马返回了1
copy
2 --copy 索引1的值为2,由于不是表,立马返回 了2
copy
inside --copy 索引 为 inside ,这里一定是copy成功了,key和value是分开的,所有的key都是非表结构,然后变为 新表的一个key
copy
table: 00E882C8 --开始copy inside的value,是个表,相当于又创立个新表inside,又逐key赋值到新表
copy
y --inside key 为y
copy
2 -inside key为y 的值 为2
index:1 --新的inside创建好了,返回,因为记忆表已经创建好了 key为,tabA的table,key为inside的table
table: 00E880C0
table: 00E880E8
copy --接着遍历,复制key 为x
x
copy
1
index:2
pairs遍历顺序
代码语言:javascript复制tabA = { 1,x= 2,inside = { y =3},{4,5}}
for index, value in pairs(tabA) do
if (type(index) ~= "table") then
print( index)
print( ":")
print( value)
end
end
print(tabA[2][2])
输出
代码语言:javascript复制1
:
1
2
:
table: 00ED8B78
inside
:
table: 00ED8DD0
x
:
2
5
在使用pairs函数进行打印的时候,先打印表中的值,再按照键值对的键所对应的哈希值进行打印,后面的顺序是哈希顺序,并不是字母顺序
字符串形式输出表中的内容
代码语言:javascript复制--tb:表
--dump_metatable:是否打印元表
--max_level:打印的层级,越大能打印更多嵌套表
local function dump(tb, dump_metatable, max_level)
local lookup_table = {}
local level = 0
local rep = string.rep
local dump_metatable = dump_metatable
local max_level = max_level or 1
local function _dump(tb, level)
local str = "n" .. rep("t", level) .. "{n"
for k,v in pairs(tb) do
local k_is_str = type(k) == "string" and 1 or 0
local v_is_str = type(v) == "string" and 1 or 0
str = str..rep("t", level 1).."["..rep(""", k_is_str)..(tostring(k) or type(k))..rep(""", k_is_str).."]".." = "
if type(v) == "table" then
if not lookup_table[v] and ((not max_level) or level < max_level) then
lookup_table[v] = true
str = str.._dump(v, level 1, dump_metatable).."n"
else
str = str..(tostring(v) or type(v))..",n"
end
else
str = str..rep(""", v_is_str)..(tostring(v) or type(v))..rep(""", v_is_str)..",n"
end
end
if dump_metatable then
local mt = getmetatable(tb)
if mt ~= nil and type(mt) == "table" then
str = str..rep("t", level 1).."["__metatable"]".." = "
if not lookup_table[mt] and ((not max_level) or level < max_level) then
lookup_table[mt] = true
str = str.._dump(mt, level 1, dump_metatable).."n"
else
str = str..(tostring(v) or type(v))..",n"
end
end
end
str = str..rep("t", level) .. "},"
return str
end
return _dump(tb, level)
end
tabA = {1,2,x = 3,4,{5,6}}
print(dump(tabA))
输出
代码语言:javascript复制{
[1] = 1,
[2] = 2,
[3] = 4,
[4] =
{
[1] = 5,
[2] = 6,
},
["x"] = 3,
},
代码解析:
- _dump中pairs遍历表tb,如果是v的类型是table,如果没遍历过,且深度<最大深度,遍历v 的table
- 如果v的类型是普通域,直接字符串叠加
- 全部执行完子类的k-v,接着执行查找元表。按照1,2顺序再来一遍