My deep copy code:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
I'm trying to implement this to oop using self
, but couldn't get it to work, here is what I've tried so far
function block:deepcopy()
local orig_type = type(self)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, self, nil do
copy[self:deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, self:deeepcopy(getmetatable(self)))
else
copy = orig
end
return copy
In the OOP version of the function, self:deepcopy(something)
with the method syntax (colon) doesn't do what you want it to. It is equivalent to self.deepcopy(self, something)
; the second argument something
is ignored and you just end up trying to re-copy the same self
over and over until there's a stack overflow. You have to do self.deepcopy(something)
with a dot to pass something
as the self
argument (the argument that is copied).
Calling self.deepcopy
inside the definition of the deepcopy
method assumes that every subtable has a self.deepcopy
function. If not, you will get an "attempt to call a nil value" error. But you could do this if you want every subtable to have its own version of deepcopy
that is used when copying the immediate children of that table (keys, values, metatable). For instance, you could have a subtable whose deepcopy
method does not copy the metatable. Here is the basic version where the subtable has the same deepcopy
method:
local block = {}
function block:deepcopy()
if type(self) == 'table' then
local copy = {}
for key, value in pairs(self) do
copy[self.deepcopy(key)] = self.deepcopy(value)
end
return setmetatable(copy, self.deepcopy(getmetatable(self)))
else
return self
end
end
block.a = { a = 10, deepcopy = block.deepcopy }
block:deepcopy() -- works
block.a = { a = 10 }
block:deepcopy() -- error: "attempt to call a nil value (field 'deepcopy')"
But you don't need to rewrite the function at all to use it in object-oriented style. Try using your first definition of deepcopy
. Do object.deepcopy = deepcopy
, and then call object:deepcopy()
and you will get a copy of the object.