From 21863e7e0890fa3f55e9efd85a0746d312e7dc53 Mon Sep 17 00:00:00 2001 From: Tom Christiansen Date: Mon, 2 May 2011 09:24:49 -0400 Subject: [PATCH] perllol doc updates from tchrist. --- pod/perllol.pod | 157 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 121 insertions(+), 36 deletions(-) diff --git a/pod/perllol.pod b/pod/perllol.pod index 58d532b..8c6c056 100644 --- a/pod/perllol.pod +++ b/pod/perllol.pod @@ -6,23 +6,24 @@ perllol - Manipulating Arrays of Arrays in Perl =head2 Declaration and Access of Arrays of Arrays -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. +The simplest two-level data structure to build in Perl is an array of +arrays, sometimes casually 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. 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: + use 5.010; # so we can use say() + # assign to our array, an array of array references @AoA = ( - [ "fred", "barney" ], - [ "george", "jane", "elroy" ], - [ "homer", "marge", "bart" ], + [ "fred", "barney", "pebbles", "bambam", "dino", ], + [ "george", "jane", "elroy", "judy", ], + [ "homer", "bart", "marge", "maggie", ], ); - - print $AoA[2][2]; + say $AoA[2][1]; bart Now you should be very careful that the outer bracket type @@ -33,11 +34,11 @@ but rather just a reference to it, you could do something more like this: # assign a reference to array of array references $ref_to_AoA = [ [ "fred", "barney", "pebbles", "bambam", "dino", ], - [ "homer", "bart", "marge", "maggie", ], [ "george", "jane", "elroy", "judy", ], + [ "homer", "bart", "marge", "maggie", ], ]; - - print $ref_to_AoA->[2][2]; + say $ref_to_AoA->[2][1]; + bart 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 @@ -88,16 +89,18 @@ array in it. $AoA[$i] = [ @tmp ]; } -It's very important that you make sure to use the C<[]> array reference -constructor. That's because this will be very wrong: +It's important you make sure to use the C<[ ]> array reference +constructor. That's because this wouldn't work: - $AoA[$i] = @tmp; + $AoA[$i] = @tmp; # WRONG! -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. +The reason that doesn't do what you want is because assigning a +named array like that to a scalar is taking an array in scalar +context, which means just counts the number of elements in @tmp. -If you are running under C, you'll have to add some -declarations to make it happy: +If you are running under C (and if you aren't, why in +the world aren't you?), you'll have to add some declarations to +make it happy: use strict; my(@AoA, @tmp); @@ -118,14 +121,14 @@ if you knew where you wanted to put it: my (@AoA, $i, $line); for $i ( 0 .. 10 ) { $line = <>; - $AoA[$i] = [ split ' ', $line ]; + $AoA[$i] = [ split " ", $line ]; } or even just my (@AoA, $i); for $i ( 0 .. 10 ) { - $AoA[$i] = [ split ' ', <> ]; + $AoA[$i] = [ split " ", <> ]; } You should in general be leery of using functions that could @@ -134,7 +137,7 @@ such. This would be clearer to the casual reader: my (@AoA, $i); for $i ( 0 .. 10 ) { - $AoA[$i] = [ split ' ', scalar(<>) ]; + $AoA[$i] = [ split " ", scalar(<>) ]; } If you wanted to have a $ref_to_AoA variable as a reference to an array, @@ -165,14 +168,45 @@ 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 @{ $AoA[0] }, "wilma", "betty"; + push @{ $AoA[0] }, "wilma", "betty"; # explicit deref + +Prior to Perl 5.14, this wouldn't even compile: + + push $AoA[0], "wilma", "betty"; # implicit deref + +How come? Because once upon a time, the argument to push() had to be be a +real array, not just a reference to one. That's no longer true. In fact, +the line marked "implicit deref" above works just fine--in this +instance--to do what the one that says explicit deref did. + +The reason I said "in this instance" is because that I works +because C<$AoA[0]> already held an array reference. If you try that on an +undefined variable, you'll take an exception. That's because the implicit +derefererence will never autovivify an undefined variable the way C<@{ }> +always will: -Notice that I I say just: + my $aref = undef; + push $aref, qw(some more values); # WRONG! + push @$aref, qw(a few more); # ok - push $AoA[0], "wilma", "betty"; # WRONG! +If you want to take advantage of this new implicit dereferencing behavior, +go right ahead: it makes code easier on the eye and wrist. Just understand +that older releases will choke on it during compilation. Whenever you make +use of something that works only in some given release of Perl and later, +but not earlier, you should place a prominent -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. + use v5.14; # needed for implicit deref of array refs by array ops + +directive at the top of the file that needs it. That way when somebody +tries to run the new code under an old perl, rather than getting an error like + + Type of arg 1 to push must be array (not array element) at /tmp/a line 8, near ""betty";" + Execution of /tmp/a aborted due to compilation errors. + +they'll be politely informed that + + Perl v5.14.0 required--this is only v5.12.3, stopped at /tmp/a line 1. + BEGIN failed--compilation aborted at /tmp/a line 1. =head2 Access and Printing @@ -194,20 +228,20 @@ using the shell-style for() construct to loop across the outer set of subscripts. for $aref ( @AoA ) { - print "\t [ @$aref ],\n"; + say "\t [ @$aref ],"; } If you wanted to keep track of subscripts, you might do this: for $i ( 0 .. $#AoA ) { - print "\t elt $i is [ @{$AoA[$i]} ],\n"; + say "\t elt $i is [ @{$AoA[$i]} ],"; } or maybe even this. Notice the inner loop. for $i ( 0 .. $#AoA ) { for $j ( 0 .. $#{$AoA[$i]} ) { - print "elt $i $j is $AoA[$i][$j]\n"; + say "elt $i $j is $AoA[$i][$j]"; } } @@ -217,7 +251,7 @@ sometimes is easier to take a temporary on your way through: for $i ( 0 .. $#AoA ) { $aref = $AoA[$i]; for $j ( 0 .. $#{$aref} ) { - print "elt $i $j is $AoA[$i][$j]\n"; + say "elt $i $j is $AoA[$i][$j]"; } } @@ -227,18 +261,65 @@ Hmm... that's still a bit ugly. How about this: $aref = $AoA[$i]; $n = @$aref - 1; for $j ( 0 .. $n ) { - print "elt $i $j is $AoA[$i][$j]\n"; + say "elt $i $j is $AoA[$i][$j]"; } } +When you get tired of writing a custom print for your data structures, +you might look at the standard L or L modules. +The former is what the Perl debugger uses, while the latter generates +parsable Perl code. For example: + + use v5.14; # using the + prototype, new to v5.14 + + sub show(+) { + require Dumpvalue; + state $prettily = new Dumpvalue:: + tick => q("), + compactDump => 1, # comment these two lines out + veryCompact => 1, # if you want a bigger dump + ; + dumpValue $prettily @_; + } + + # Assign a list of array references to an array. + my @AoA = ( + [ "fred", "barney" ], + [ "george", "jane", "elroy" ], + [ "homer", "marge", "bart" ], + ); + push $AoA[0], "wilma", "betty"; + show @AoA; + +will print out: + + 0 0..3 "fred" "barney" "wilma" "betty" + 1 0..2 "george" "jane" "elroy" + 2 0..2 "homer" "marge" "bart" + +Whereas if you comment out the two lines I said you might wish to, +then it shows it to you this way instead: + + 0 ARRAY(0x8031d0) + 0 "fred" + 1 "barney" + 2 "wilma" + 3 "betty" + 1 ARRAY(0x803d40) + 0 "george" + 1 "jane" + 2 "elroy" + 2 ARRAY(0x803e10) + 0 "homer" + 1 "marge" + 2 "bart" + =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 because while we have a nice synonym for single elements via the 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 @AoA variable as before. @@ -251,9 +332,13 @@ variable as before. That same loop could be replaced with a slice operation: + @part = @{$AoA[4]}[7..12]; + +or spaced out a bit: + @part = @{ $AoA[4] } [ 7..12 ]; -but as you might well imagine, this is pretty rough on the reader. +But as you might well imagine, this can get 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? Hmm... here's the simple way: @@ -300,4 +385,4 @@ L, L, L Tom Christiansen > -Last update: Thu Jun 4 16:16:23 MDT 1998 +Last update: Tue Apr 26 18:30:55 MDT 2011 -- 2.7.4