Slice() is a Lie!

Today I tripped over a long forgotten Actionscript woe: De-Referencing arrays. What'll happen is, you'll slice() yourself off a copy of some multidimensional array, then later you'll change one of the values of your duplicate only to find it isn't really a duplicate, and the original it was slice()'d from has changed as well.

The problem is that slice() and concat() apparently only make true duplicates of the top level of an Array. So when you slice() off a 2D array, you end up with a genuine copy of that array - sure, sure - but what you've copied is really just a bunch of shallow references to the original.

Here's some code to try and make sense of it:

var sacred:Array = [ [0,1,2], [3,4,5] ];
var heretical:Array = sacred.slice();

heretical[0][0] = 99;

trace(heretical); //99,1,2,3,4,5
trace(sacred); //99,1,2,3,4,5

Mind you that if you alter the reference itself, say by changing the value of heretical[0] rather than going straight down to [0][0], you will have replaced the path to 'sacred' and heretical[0] will be thereafter wholly independent from sacred[0], such that any changes to heretical[0][0] will nolonger be reflected in 'sacred.'

It's a really old problem in Actionscript made new again by the Vector class brought in with Flash 10 and AS3. The Vector class is, after all, little more than a type-specified Array(), and so it is subject to the same slice() and concat() pitfalls. But in practice it's easy not to think of your Vectors as Arrays and that's how I managed to butt heads with slice() again today.

Here's an example of the slice issue using the Vector class:

var sacred:Vector.<Vector.<int>> = new Vector.<Vector.<int>>;
var heretical:Vector.<Vector.<int>> = new Vector.<Vector.<int>>;
var vInt:Vector.<int> = new Vector.<int>;

vInt.push(0,1,2, 3,4,5);
sacred.push( vInt.slice(0,3) );
sacred.push( vInt.slice(3,6) );

heretical = sacred.slice();

heretical[0][0] = 99;

trace(heretical); //99,1,2,3,4,5
trace(sacred); //99,1,2,3,4,5

Once you get deep into your own code - when your eyes have glossed over from too much trigonometry and micromanaging nested loops - it may not be immediately obvious to you that your Vector.<Vector.<int>> is, at end, an Array containing an Array containing some integers, or Array[Array[int]]. And that any duplication of that top level array really only passes along the inner array, which is still just a collection of references.

I don't know why Adobe hasn't provided a deep copy method for Arrays by now. I understand that the issue presented here isn't really a bug; that slice() is doing just what it says in the livedocs: Returning "a new array that consists of a range of elements from the original array, without modifying the original array." And that the shallow-references we end up with are the "elements" of the original array, as stated. Still, how many people have to bump their heads on the ceiling before Adobe integrates a method of really, truly, no shit, deep-copying an array? I suggest Array.noShitCopy();

0 comments:

Post a Comment