X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=pod%2Fperllol.pod;h=c348ec43b2bbe24f6f878eaf6f4f1facd5d141e6;hb=4447dfc12157eb387a12bf297bbc4f4c8c49a7a6;hp=a1e8a2deef10dea5ba9bf159cf3a667865c1e624;hpb=4d9142afa100a96f07b67cd4b087273df8c60543;p=p5sagit%2Fp5-mst-13.2.git diff --git a/pod/perllol.pod b/pod/perllol.pod index a1e8a2d..c348ec4 100644 --- a/pod/perllol.pod +++ b/pod/perllol.pod @@ -1,64 +1,64 @@ =head1 NAME -perlLoL - Manipulating Lists of Lists in Perl +perllol - Manipulating Arrays of Arrays in Perl =head1 DESCRIPTION -=head1 Declaration and Access of Lists of Lists +=head2 Declaration and Access of Arrays of Arrays -The simplest thing to build is a list of lists (sometimes called an array -of arrays). It's reasonably easy to understand, and almost everything -that applies here will also be applicable later on with the fancier data -structures. +The simplest thing to build is an array of arrays (sometimes imprecisely +called a list of lists). It's reasonably easy to understand, and +almost everything that applies here will also be applicable later +on with the fancier data structures. -A list of lists, or an array of an array if you would, is just a regular -old array @LoL that you can get at with two subscripts, like $LoL[3][2]. Here's -a declaration of the array: +An array of an array is just a regular old array @AoA that you can +get at with two subscripts, like C<$AoA[3][2]>. Here's a declaration +of the array: - # assign to our array a list of list references - @LoL = ( + # assign to our array, an array of array references + @AoA = ( [ "fred", "barney" ], [ "george", "jane", "elroy" ], [ "homer", "marge", "bart" ], ); - print $LoL[2][2]; + print $AoA[2][2]; bart Now you should be very careful that the outer bracket type -is a round one, that is, parentheses. That's because you're assigning to -an @list, so you need parens. If you wanted there I to be an @LoL, +is a round one, that is, a parenthesis. That's because you're assigning to +an @array, so you need parentheses. If you wanted there I to be an @AoA, but rather just a reference to it, you could do something more like this: - # assign a reference to list of list references - $ref_to_LoL = [ + # assign a reference to array of array references + $ref_to_AoA = [ [ "fred", "barney", "pebbles", "bambam", "dino", ], [ "homer", "bart", "marge", "maggie", ], - [ "george", "jane", "alroy", "judy", ], + [ "george", "jane", "elroy", "judy", ], ]; - print $ref_to_LoL->[2][2]; + print $ref_to_AoA->[2][2]; -Notice that the outer bracket type has changed, and so our access syntax +Notice that the outer bracket type has changed, and so our access syntax has also changed. That's because unlike C, in perl you can't freely -interchange arrays and references thereto. $ref_to_LoL is a reference to an -array, whereas @LoL is an array proper. Likewise, $LoL[2] is not an +interchange arrays and references thereto. $ref_to_AoA is a reference to an +array, whereas @AoA is an array proper. Likewise, C<$AoA[2]> is not an array, but an array ref. So how come you can write these: - $LoL[2][2] - $ref_to_LoL->[2][2] + $AoA[2][2] + $ref_to_AoA->[2][2] instead of having to write these: - $LoL[2]->[2] - $ref_to_LoL->[2]->[2] + $AoA[2]->[2] + $ref_to_AoA->[2]->[2] Well, that's because the rule is that on adjacent brackets only (whether -square or curly), you are free to omit the pointer dereferencing array. +square or curly), you are free to omit the pointer dereferencing arrow. But you cannot do so for the very first one if it's a scalar containing -a reference, which means that $ref_to_LoL always needs it. +a reference, which means that $ref_to_AoA always needs it. -=head1 Growing Your Own +=head2 Growing Your Own That's all well and good for declaration of a fixed data structure, but what if you wanted to add new elements on the fly, or build @@ -67,181 +67,171 @@ it up entirely from scratch? First, let's look at reading it in from a file. This is something like adding a row at a time. We'll assume that there's a flat file in which each line is a row and each word an element. If you're trying to develop an -@LoL list containing all these, here's the right way to do that: +@AoA array containing all these, here's the right way to do that: while (<>) { @tmp = split; - push @LoL, [ @tmp ]; - } + push @AoA, [ @tmp ]; + } You might also have loaded that from a function: for $i ( 1 .. 10 ) { - $LoL[$i] = [ somefunc($i) ]; + $AoA[$i] = [ somefunc($i) ]; } Or you might have had a temporary variable sitting around with the -list in it. +array in it. for $i ( 1 .. 10 ) { @tmp = somefunc($i); - $LoL[$i] = [ @tmp ]; + $AoA[$i] = [ @tmp ]; } -It's very important that you make sure to use the C<[]> list reference +It's very important that you make sure to use the C<[]> array reference constructor. That's because this will be very wrong: - $LoL[$i] = @tmp; + $AoA[$i] = @tmp; -You see, assigning a named list like that to a scalar just counts the -number of elements in @tmp, which probably isn't what you want. +You see, assigning a named array like that to a scalar just counts the +number of elements in @tmp, which probably isn't what you want. If you are running under C, you'll have to add some declarations to make it happy: use strict; - my(@LoL, @tmp); + my(@AoA, @tmp); while (<>) { @tmp = split; - push @LoL, [ @tmp ]; - } + push @AoA, [ @tmp ]; + } Of course, you don't need the temporary array to have a name at all: while (<>) { - push @LoL, [ split ]; - } + push @AoA, [ split ]; + } You also don't have to use push(). You could just make a direct assignment if you knew where you wanted to put it: - my (@LoL, $i, $line); - for $i ( 0 .. 10 ) + my (@AoA, $i, $line); + for $i ( 0 .. 10 ) { $line = <>; - $LoL[$i] = [ split ' ', $line ]; - } + $AoA[$i] = [ split ' ', $line ]; + } or even just - my (@LoL, $i); - for $i ( 0 .. 10 ) - $LoL[$i] = [ split ' ', <> ]; - } + my (@AoA, $i); + for $i ( 0 .. 10 ) { + $AoA[$i] = [ split ' ', <> ]; + } -You should in general be leery of using potential list functions -in a scalar context without explicitly stating such. -This would be clearer to the casual reader: +You should in general be leery of using functions that could +potentially return lists in scalar context without explicitly stating +such. This would be clearer to the casual reader: - my (@LoL, $i); - for $i ( 0 .. 10 ) - $LoL[$i] = [ split ' ', scalar(<>) ]; - } + my (@AoA, $i); + for $i ( 0 .. 10 ) { + $AoA[$i] = [ split ' ', scalar(<>) ]; + } -If you wanted to have a $ref_to_LoL variable as a reference to an array, +If you wanted to have a $ref_to_AoA variable as a reference to an array, you'd have to do something like this: while (<>) { - push @$ref_to_LoL, [ split ]; - } - -Actually, if you were using strict, you'd not only have to declare $ref_to_LoL as -you had to declare @LoL, but you'd I having to initialize it to a -reference to an empty list. (This was a bug in 5.001m that's been fixed -for the 5.002 release.) - - my $ref_to_LoL = []; - while (<>) { - push @$ref_to_LoL, [ split ]; - } + push @$ref_to_AoA, [ split ]; + } -Ok, now you can add new rows. What about adding new columns? If you're -just dealing with matrices, it's often easiest to use simple assignment: +Now you can add new rows. What about adding new columns? If you're +dealing with just matrices, it's often easiest to use simple assignment: for $x (1 .. 10) { for $y (1 .. 10) { - $LoL[$x][$y] = func($x, $y); + $AoA[$x][$y] = func($x, $y); } } for $x ( 3, 7, 9 ) { - $LoL[$x][20] += func2($x); - } + $AoA[$x][20] += func2($x); + } -It doesn't matter whether those elements are already +It doesn't matter whether those elements are already there or not: it'll gladly create them for you, setting intervening elements to C as need be. -If you just wanted to append to a row, you'd have +If you wanted just to append to a row, you'd have to do something a bit funnier looking: # add new columns to an existing row - push @{ $LoL[0] }, "wilma", "betty"; + push @{ $AoA[0] }, "wilma", "betty"; -Notice that I I just say: +Notice that I I say just: - push $LoL[0], "wilma", "betty"; # WRONG! + push $AoA[0], "wilma", "betty"; # WRONG! In fact, that wouldn't even compile. How come? Because the argument to push() must be a real array, not just a reference to such. -=head1 Access and Printing +=head2 Access and Printing -Now it's time to print your data structure out. How -are you going to do that? Well, if you only want one +Now it's time to print your data structure out. How +are you going to do that? Well, if you want only one of the elements, it's trivial: - print $LoL[0][0]; + print $AoA[0][0]; If you want to print the whole thing, though, you can't -just say +say - print @LoL; # WRONG + print @AoA; # WRONG -because you'll just get references listed, and perl will never -automatically dereference things for you. Instead, you have to +because you'll get just references listed, and perl will never +automatically dereference things for you. Instead, you have to roll yourself a loop or two. This prints the whole structure, using the shell-style for() construct to loop across the outer -set of subscripts. +set of subscripts. - for $aref ( @LoL ) { + for $aref ( @AoA ) { print "\t [ @$aref ],\n"; } If you wanted to keep track of subscripts, you might do this: - for $i ( 0 .. $#LoL ) { - print "\t elt $i is [ @{$LoL[$i]} ],\n"; + for $i ( 0 .. $#AoA ) { + print "\t elt $i is [ @{$AoA[$i]} ],\n"; } or maybe even this. Notice the inner loop. - for $i ( 0 .. $#LoL ) { - for $j ( 0 .. $#{$LoL[$i]} ) { - print "elt $i $j is $LoL[$i][$j]\n"; + for $i ( 0 .. $#AoA ) { + for $j ( 0 .. $#{$AoA[$i]} ) { + print "elt $i $j is $AoA[$i][$j]\n"; } } -As you can see, it's getting a bit complicated. That's why +As you can see, it's getting a bit complicated. That's why sometimes is easier to take a temporary on your way through: - for $i ( 0 .. $#LoL ) { - $aref = $LoL[$i]; + for $i ( 0 .. $#AoA ) { + $aref = $AoA[$i]; for $j ( 0 .. $#{$aref} ) { - print "elt $i $j is $LoL[$i][$j]\n"; + print "elt $i $j is $AoA[$i][$j]\n"; } } -Hm... that's still a bit ugly. How about this: +Hmm... that's still a bit ugly. How about this: - for $i ( 0 .. $#LoL ) { - $aref = $LoL[$i]; + for $i ( 0 .. $#AoA ) { + $aref = $AoA[$i]; $n = @$aref - 1; for $j ( 0 .. $n ) { - print "elt $i $j is $LoL[$i][$j]\n"; + print "elt $i $j is $AoA[$i][$j]\n"; } } -=head1 Slices +=head2 Slices If you want to get at a slice (part of a row) in a multidimensional array, you're going to have to do some fancy subscripting. That's @@ -250,56 +240,56 @@ pointer arrow for dereferencing, no such convenience exists for slices. (Remember, of course, that you can always write a loop to do a slice operation.) -Here's how to do one operation using a loop. We'll assume an @LoL +Here's how to do one operation using a loop. We'll assume an @AoA variable as before. @part = (); - $x = 4; + $x = 4; for ($y = 7; $y < 13; $y++) { - push @part, $LoL[$x][$y]; - } + push @part, $AoA[$x][$y]; + } That same loop could be replaced with a slice operation: - @part = @{ $LoL[4] } [ 7..12 ]; + @part = @{ $AoA[4] } [ 7..12 ]; but as you might well imagine, this is pretty rough on the reader. Ah, but what if you wanted a I, such as having -$x run from 4..8 and $y run from 7 to 12? Hm... here's the simple way: +$x run from 4..8 and $y run from 7 to 12? Hmm... here's the simple way: - @newLoL = (); + @newAoA = (); for ($startx = $x = 4; $x <= 8; $x++) { - for ($starty = $y = 7; $x <= 12; $y++) { - $newLoL[$x - $startx][$y - $starty] = $LoL[$x][$y]; + for ($starty = $y = 7; $y <= 12; $y++) { + $newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y]; } - } + } -We can reduce some of the looping through slices +We can reduce some of the looping through slices for ($x = 4; $x <= 8; $x++) { - push @newLoL, [ @{ $LoL[$x] } [ 7..12 ] ]; + push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ]; } If you were into Schwartzian Transforms, you would probably have selected map for that - @newLoL = map { [ @{ $LoL[$_] } [ 7..12 ] ] } 4 .. 8; + @newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8; Although if your manager accused of seeking job security (or rapid insecurity) through inscrutable code, it would be hard to argue. :-) If I were you, I'd put that in a function: - @newLoL = splice_2D( \@LoL, 4 => 8, 7 => 12 ); + @newAoA = splice_2D( \@AoA, 4 => 8, 7 => 12 ); sub splice_2D { - my $lrr = shift; # ref to list of list refs! - my ($x_lo, $x_hi, + my $lrr = shift; # ref to array of array refs! + my ($x_lo, $x_hi, $y_lo, $y_hi) = @_; - return map { - [ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ] + return map { + [ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ] } $x_lo .. $x_hi; - } + } =head1 SEE ALSO @@ -308,6 +298,6 @@ perldata(1), perlref(1), perldsc(1) =head1 AUTHOR -Tom Christiansen +Tom Christiansen > -Last udpate: Sat Oct 7 19:35:26 MDT 1995 +Last update: Thu Jun 4 16:16:23 MDT 1998