1=head1 NAME 2 3perllol - Manipulating Arrays of Arrays in Perl 4 5=head1 DESCRIPTION 6 7=head2 Declaration and Access of Arrays of Arrays 8 9The simplest thing to build is an array of arrays (sometimes imprecisely 10called a list of lists). It's reasonably easy to understand, and 11almost everything that applies here will also be applicable later 12on with the fancier data structures. 13 14An array of an array is just a regular old array @AoA that you can 15get at with two subscripts, like C<$AoA[3][2]>. Here's a declaration 16of the array: 17 18 # assign to our array, an array of array references 19 @AoA = ( 20 [ "fred", "barney" ], 21 [ "george", "jane", "elroy" ], 22 [ "homer", "marge", "bart" ], 23 ); 24 25 print $AoA[2][2]; 26 bart 27 28Now you should be very careful that the outer bracket type 29is a round one, that is, a parenthesis. That's because you're assigning to 30an @array, so you need parentheses. If you wanted there I<not> to be an @AoA, 31but rather just a reference to it, you could do something more like this: 32 33 # assign a reference to array of array references 34 $ref_to_AoA = [ 35 [ "fred", "barney", "pebbles", "bambam", "dino", ], 36 [ "homer", "bart", "marge", "maggie", ], 37 [ "george", "jane", "elroy", "judy", ], 38 ]; 39 40 print $ref_to_AoA->[2][2]; 41 42Notice that the outer bracket type has changed, and so our access syntax 43has also changed. That's because unlike C, in perl you can't freely 44interchange arrays and references thereto. $ref_to_AoA is a reference to an 45array, whereas @AoA is an array proper. Likewise, C<$AoA[2]> is not an 46array, but an array ref. So how come you can write these: 47 48 $AoA[2][2] 49 $ref_to_AoA->[2][2] 50 51instead of having to write these: 52 53 $AoA[2]->[2] 54 $ref_to_AoA->[2]->[2] 55 56Well, that's because the rule is that on adjacent brackets only (whether 57square or curly), you are free to omit the pointer dereferencing arrow. 58But you cannot do so for the very first one if it's a scalar containing 59a reference, which means that $ref_to_AoA always needs it. 60 61=head2 Growing Your Own 62 63That's all well and good for declaration of a fixed data structure, 64but what if you wanted to add new elements on the fly, or build 65it up entirely from scratch? 66 67First, let's look at reading it in from a file. This is something like 68adding a row at a time. We'll assume that there's a flat file in which 69each line is a row and each word an element. If you're trying to develop an 70@AoA array containing all these, here's the right way to do that: 71 72 while (<>) { 73 @tmp = split; 74 push @AoA, [ @tmp ]; 75 } 76 77You might also have loaded that from a function: 78 79 for $i ( 1 .. 10 ) { 80 $AoA[$i] = [ somefunc($i) ]; 81 } 82 83Or you might have had a temporary variable sitting around with the 84array in it. 85 86 for $i ( 1 .. 10 ) { 87 @tmp = somefunc($i); 88 $AoA[$i] = [ @tmp ]; 89 } 90 91It's very important that you make sure to use the C<[]> array reference 92constructor. That's because this will be very wrong: 93 94 $AoA[$i] = @tmp; 95 96You see, assigning a named array like that to a scalar just counts the 97number of elements in @tmp, which probably isn't what you want. 98 99If you are running under C<use strict>, you'll have to add some 100declarations to make it happy: 101 102 use strict; 103 my(@AoA, @tmp); 104 while (<>) { 105 @tmp = split; 106 push @AoA, [ @tmp ]; 107 } 108 109Of course, you don't need the temporary array to have a name at all: 110 111 while (<>) { 112 push @AoA, [ split ]; 113 } 114 115You also don't have to use push(). You could just make a direct assignment 116if you knew where you wanted to put it: 117 118 my (@AoA, $i, $line); 119 for $i ( 0 .. 10 ) { 120 $line = <>; 121 $AoA[$i] = [ split ' ', $line ]; 122 } 123 124or even just 125 126 my (@AoA, $i); 127 for $i ( 0 .. 10 ) { 128 $AoA[$i] = [ split ' ', <> ]; 129 } 130 131You should in general be leery of using functions that could 132potentially return lists in scalar context without explicitly stating 133such. This would be clearer to the casual reader: 134 135 my (@AoA, $i); 136 for $i ( 0 .. 10 ) { 137 $AoA[$i] = [ split ' ', scalar(<>) ]; 138 } 139 140If you wanted to have a $ref_to_AoA variable as a reference to an array, 141you'd have to do something like this: 142 143 while (<>) { 144 push @$ref_to_AoA, [ split ]; 145 } 146 147Now you can add new rows. What about adding new columns? If you're 148dealing with just matrices, it's often easiest to use simple assignment: 149 150 for $x (1 .. 10) { 151 for $y (1 .. 10) { 152 $AoA[$x][$y] = func($x, $y); 153 } 154 } 155 156 for $x ( 3, 7, 9 ) { 157 $AoA[$x][20] += func2($x); 158 } 159 160It doesn't matter whether those elements are already 161there or not: it'll gladly create them for you, setting 162intervening elements to C<undef> as need be. 163 164If you wanted just to append to a row, you'd have 165to do something a bit funnier looking: 166 167 # add new columns to an existing row 168 push @{ $AoA[0] }, "wilma", "betty"; 169 170Notice that I I<couldn't> say just: 171 172 push $AoA[0], "wilma", "betty"; # WRONG! 173 174In fact, that wouldn't even compile. How come? Because the argument 175to push() must be a real array, not just a reference to such. 176 177=head2 Access and Printing 178 179Now it's time to print your data structure out. How 180are you going to do that? Well, if you want only one 181of the elements, it's trivial: 182 183 print $AoA[0][0]; 184 185If you want to print the whole thing, though, you can't 186say 187 188 print @AoA; # WRONG 189 190because you'll get just references listed, and perl will never 191automatically dereference things for you. Instead, you have to 192roll yourself a loop or two. This prints the whole structure, 193using the shell-style for() construct to loop across the outer 194set of subscripts. 195 196 for $aref ( @AoA ) { 197 print "\t [ @$aref ],\n"; 198 } 199 200If you wanted to keep track of subscripts, you might do this: 201 202 for $i ( 0 .. $#AoA ) { 203 print "\t elt $i is [ @{$AoA[$i]} ],\n"; 204 } 205 206or maybe even this. Notice the inner loop. 207 208 for $i ( 0 .. $#AoA ) { 209 for $j ( 0 .. $#{$AoA[$i]} ) { 210 print "elt $i $j is $AoA[$i][$j]\n"; 211 } 212 } 213 214As you can see, it's getting a bit complicated. That's why 215sometimes is easier to take a temporary on your way through: 216 217 for $i ( 0 .. $#AoA ) { 218 $aref = $AoA[$i]; 219 for $j ( 0 .. $#{$aref} ) { 220 print "elt $i $j is $AoA[$i][$j]\n"; 221 } 222 } 223 224Hmm... that's still a bit ugly. How about this: 225 226 for $i ( 0 .. $#AoA ) { 227 $aref = $AoA[$i]; 228 $n = @$aref - 1; 229 for $j ( 0 .. $n ) { 230 print "elt $i $j is $AoA[$i][$j]\n"; 231 } 232 } 233 234=head2 Slices 235 236If you want to get at a slice (part of a row) in a multidimensional 237array, you're going to have to do some fancy subscripting. That's 238because while we have a nice synonym for single elements via the 239pointer arrow for dereferencing, no such convenience exists for slices. 240(Remember, of course, that you can always write a loop to do a slice 241operation.) 242 243Here's how to do one operation using a loop. We'll assume an @AoA 244variable as before. 245 246 @part = (); 247 $x = 4; 248 for ($y = 7; $y < 13; $y++) { 249 push @part, $AoA[$x][$y]; 250 } 251 252That same loop could be replaced with a slice operation: 253 254 @part = @{ $AoA[4] } [ 7..12 ]; 255 256but as you might well imagine, this is pretty rough on the reader. 257 258Ah, but what if you wanted a I<two-dimensional slice>, such as having 259$x run from 4..8 and $y run from 7 to 12? Hmm... here's the simple way: 260 261 @newAoA = (); 262 for ($startx = $x = 4; $x <= 8; $x++) { 263 for ($starty = $y = 7; $y <= 12; $y++) { 264 $newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y]; 265 } 266 } 267 268We can reduce some of the looping through slices 269 270 for ($x = 4; $x <= 8; $x++) { 271 push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ]; 272 } 273 274If you were into Schwartzian Transforms, you would probably 275have selected map for that 276 277 @newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8; 278 279Although if your manager accused of seeking job security (or rapid 280insecurity) through inscrutable code, it would be hard to argue. :-) 281If I were you, I'd put that in a function: 282 283 @newAoA = splice_2D( \@AoA, 4 => 8, 7 => 12 ); 284 sub splice_2D { 285 my $lrr = shift; # ref to array of array refs! 286 my ($x_lo, $x_hi, 287 $y_lo, $y_hi) = @_; 288 289 return map { 290 [ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ] 291 } $x_lo .. $x_hi; 292 } 293 294 295=head1 SEE ALSO 296 297perldata(1), perlref(1), perldsc(1) 298 299=head1 AUTHOR 300 301Tom Christiansen <F<tchrist@perl.com>> 302 303Last update: Thu Jun 4 16:16:23 MDT 1998 304