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