[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 =head1 NAME 2 3 perllol - Manipulating Arrays of Arrays in Perl 4 5 =head1 DESCRIPTION 6 7 =head2 Declaration and Access of Arrays of Arrays 8 9 The simplest thing to build is an array of arrays (sometimes imprecisely 10 called a list of lists). It's reasonably easy to understand, and 11 almost everything that applies here will also be applicable later 12 on with the fancier data structures. 13 14 An array of an array is just a regular old array @AoA that you can 15 get at with two subscripts, like C<$AoA[3][2]>. Here's a declaration 16 of 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 28 Now you should be very careful that the outer bracket type 29 is a round one, that is, a parenthesis. That's because you're assigning to 30 an @array, so you need parentheses. If you wanted there I<not> to be an @AoA, 31 but 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 42 Notice that the outer bracket type has changed, and so our access syntax 43 has also changed. That's because unlike C, in perl you can't freely 44 interchange arrays and references thereto. $ref_to_AoA is a reference to an 45 array, whereas @AoA is an array proper. Likewise, C<$AoA[2]> is not an 46 array, but an array ref. So how come you can write these: 47 48 $AoA[2][2] 49 $ref_to_AoA->[2][2] 50 51 instead of having to write these: 52 53 $AoA[2]->[2] 54 $ref_to_AoA->[2]->[2] 55 56 Well, that's because the rule is that on adjacent brackets only (whether 57 square or curly), you are free to omit the pointer dereferencing arrow. 58 But you cannot do so for the very first one if it's a scalar containing 59 a reference, which means that $ref_to_AoA always needs it. 60 61 =head2 Growing Your Own 62 63 That's all well and good for declaration of a fixed data structure, 64 but what if you wanted to add new elements on the fly, or build 65 it up entirely from scratch? 66 67 First, let's look at reading it in from a file. This is something like 68 adding a row at a time. We'll assume that there's a flat file in which 69 each 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 77 You might also have loaded that from a function: 78 79 for $i ( 1 .. 10 ) { 80 $AoA[$i] = [ somefunc($i) ]; 81 } 82 83 Or you might have had a temporary variable sitting around with the 84 array in it. 85 86 for $i ( 1 .. 10 ) { 87 @tmp = somefunc($i); 88 $AoA[$i] = [ @tmp ]; 89 } 90 91 It's very important that you make sure to use the C<[]> array reference 92 constructor. That's because this will be very wrong: 93 94 $AoA[$i] = @tmp; 95 96 You see, assigning a named array like that to a scalar just counts the 97 number of elements in @tmp, which probably isn't what you want. 98 99 If you are running under C<use strict>, you'll have to add some 100 declarations to make it happy: 101 102 use strict; 103 my(@AoA, @tmp); 104 while (<>) { 105 @tmp = split; 106 push @AoA, [ @tmp ]; 107 } 108 109 Of course, you don't need the temporary array to have a name at all: 110 111 while (<>) { 112 push @AoA, [ split ]; 113 } 114 115 You also don't have to use push(). You could just make a direct assignment 116 if 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 124 or even just 125 126 my (@AoA, $i); 127 for $i ( 0 .. 10 ) { 128 $AoA[$i] = [ split ' ', <> ]; 129 } 130 131 You should in general be leery of using functions that could 132 potentially return lists in scalar context without explicitly stating 133 such. 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 140 If you wanted to have a $ref_to_AoA variable as a reference to an array, 141 you'd have to do something like this: 142 143 while (<>) { 144 push @$ref_to_AoA, [ split ]; 145 } 146 147 Now you can add new rows. What about adding new columns? If you're 148 dealing 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 160 It doesn't matter whether those elements are already 161 there or not: it'll gladly create them for you, setting 162 intervening elements to C<undef> as need be. 163 164 If you wanted just to append to a row, you'd have 165 to do something a bit funnier looking: 166 167 # add new columns to an existing row 168 push @{ $AoA[0] }, "wilma", "betty"; 169 170 Notice that I I<couldn't> say just: 171 172 push $AoA[0], "wilma", "betty"; # WRONG! 173 174 In fact, that wouldn't even compile. How come? Because the argument 175 to push() must be a real array, not just a reference to such. 176 177 =head2 Access and Printing 178 179 Now it's time to print your data structure out. How 180 are you going to do that? Well, if you want only one 181 of the elements, it's trivial: 182 183 print $AoA[0][0]; 184 185 If you want to print the whole thing, though, you can't 186 say 187 188 print @AoA; # WRONG 189 190 because you'll get just references listed, and perl will never 191 automatically dereference things for you. Instead, you have to 192 roll yourself a loop or two. This prints the whole structure, 193 using the shell-style for() construct to loop across the outer 194 set of subscripts. 195 196 for $aref ( @AoA ) { 197 print "\t [ @$aref ],\n"; 198 } 199 200 If 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 206 or 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 214 As you can see, it's getting a bit complicated. That's why 215 sometimes 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 224 Hmm... 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 236 If you want to get at a slice (part of a row) in a multidimensional 237 array, you're going to have to do some fancy subscripting. That's 238 because while we have a nice synonym for single elements via the 239 pointer arrow for dereferencing, no such convenience exists for slices. 240 (Remember, of course, that you can always write a loop to do a slice 241 operation.) 242 243 Here's how to do one operation using a loop. We'll assume an @AoA 244 variable as before. 245 246 @part = (); 247 $x = 4; 248 for ($y = 7; $y < 13; $y++) { 249 push @part, $AoA[$x][$y]; 250 } 251 252 That same loop could be replaced with a slice operation: 253 254 @part = @{ $AoA[4] } [ 7..12 ]; 255 256 but as you might well imagine, this is pretty rough on the reader. 257 258 Ah, 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 268 We 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 274 If you were into Schwartzian Transforms, you would probably 275 have selected map for that 276 277 @newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8; 278 279 Although if your manager accused of seeking job security (or rapid 280 insecurity) through inscrutable code, it would be hard to argue. :-) 281 If 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 297 perldata(1), perlref(1), perldsc(1) 298 299 =head1 AUTHOR 300 301 Tom Christiansen <F<tchrist@perl.com>> 302 303 Last update: Thu Jun 4 16:16:23 MDT 1998
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |