While copying a two dimensional array in Ruby, both
Object#clone were creating the problem of “shallow copying”. Separate copies of the array elements were created but the same reference for any Object type attributes were used inside the element. Hence, any changes to objects inside one array were showing in the copied one as well .
A solution to shallow copying seemed to be
Marshal. Forums were littered with caution against the usage of
Marshal for copying. I ignored the warning and enjoyed a working output from my implementation of Conways's Game of Life. This is how
Marshal was used:
@cell_grid = Marshal.load(Marshal.dump(@new_grid))
I introduced objects (instead of strings) as constants inside the
Cell class that was used as the basic element of an array.
The output started behaving in an unpredictable manner and I was stuck for hours before giving up and asking for help. Turned out, that the way
Marshal works is that it serialises and deserialises the objects DEAD and ALIVE and thus changes the object id.
So how do you solve the issue of copying objects or data structures in Ruby after having being let down by
Marshal? The simple answer is – you avoid copying! This may sound too harsh, but as pointed out :
“generally, if you’re trying to make a copy of an object, you’re probably thinking about the problem wrong. Copying can be avoided by exploiting the way ruby references work and designing the function APIs accordingly”.
Gregory Brown, author of Best Practises in Ruby
Inspired but still dubious, I set out to remove any instances of copying in my Game of Life implementation. Instead of copying the two dimensional grid, I changed the state of the grid for the next generation based on the current generation's grid.
We intutively use copying in our code for situations which might seem like they need copying. But reducing instances of copying will ensure less buggy code base.