Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm doing an exercise from "Intermediate Perl" -- anyway, in the below code, the YAML Dump comes out correct but the foreach (marked HERE) yields just a single array reference (ARRAY), as if tmp only had one element, although it is the exact same array.
#!/usr/bin/perl -w use strict; use YAML::XS; my %all; open(INPUT,"<coconet.dat") || die "Can't open coconet.dat"; while (<INPUT>) { if ($_ =~ /^#/) { next } my ($src, $dest, $bytes) = split; $all{$src}{$dest}+=$bytes; } close(INPUT); my @reflist = sort { ${$$b}[2] <=> ${$$a}[2] } map \&buildlist($_), ke +ys %all; foreach my $ref (@reflist) { print "${$$ref}[0] ${$$ref}[2]\n"; my @tmp=@{$$ref}[3]; #HERE foreach my $line (@tmp) { print "\t$line\n" } print Dump @tmp; } sub buildlist { my $site = $_; my @tmp; $tmp[0]=$site; $tmp[1]=\%{$all{$site}}; foreach (keys %{$tmp[1]}) { $tmp[2]+=${$tmp[1]}{$_} } @{$tmp[3]} = sort { ${$tmp[1]}{$b} <=> ${$tmp[1]}{$a} } keys %{$tm +p[1]}; return \@tmp; # 0=site name, 1=hash ref, 2=total bytes, 3=sorted + dest refs }
Why would there be this difference and what's wrong with my foreach??

Replies are listed 'Best First'.
Re: array reference madness
by almut (Canon) on Mar 14, 2009 at 21:35 UTC

    Your array @tmp holds a single array reference, not a list of values — you probably meant

    my @tmp = @{${$$ref}[3]};

    which can also be written as

    my @tmp = @{$$ref->[3]};

    BTW, I don't know what the exercise is, but I can't help thinking you have one level of referencing too much... :)

      Indeed; the extra referencing is being added by the \&buildlist($_), which is an unnecessary complication. There's no need to build a reference here at all, since buildlist() already explicitly returns one, and it's arguably confusing because \& is normally used to take a reference to a subroutine, not to a return value.

      Removing the \& there would enable all the ${$$...}-type constructions to be simplified, so for example the assignment to @tmp would then become just @{$ref->[3]}. This is probably a Good Thing.

        "Extra-referencing?!!" I was thinking the \& and the \@ matched, wrt the "type" of the return value. So you've given me something to think about -- I'll try and apply this. Thanks much! ps. the exercise is 5-2. I like the book, it's well structured ;)
      For some reason I must have thought that \& was the correct way to return a reference...anyway it worked out neatly:
      #!/usr/bin/perl -w use strict; my %all; open(INPUT,"<coconet.dat") || die "Can't open coconet.dat"; while (<INPUT>) { if ($_ =~ /^#/) { next } my ($src, $dest, $bytes) = split; # hash of hashes holds the from->to transmission TOTALS; we do not + keep all the data $all{$src}{$dest}+=$bytes; } close(INPUT); # simple use of map to create array of references to hashes: my @refli +st = map { \%{$all{$_}} } keys %all; # so...create an array of references to arrays (from map callback) of +scalars and REFS TO existing hashes and new anonymous arrays! my @reflist = sort { $$b[2] <=> $$a[2] } map buildlist($_), keys %all; + # sort by the total of TOTALS foreach my $ref (@reflist) { print "$$ref[0] $$ref[2]\n"; # the next two are the same: foreach my $line (@{$ref->[3]}) { print "\t$line ${$ref->[1]}{$li +ne}\n" } # foreach my $line (@{$$ref[3]}) { print "\t$line ${$$ref[1]}{$lin +e}\n" } } sub buildlist { my $site = $_; my @tmp; $tmp[0]=$site; $tmp[1]=\%{$all{$site}}; foreach (keys %{$tmp[1]}) { $tmp[2]+=${$tmp[1]}{$_} } # make element 3 a reference to an anonymous array of scalars (the + dest site names, ranked) @{$tmp[3]} = sort { ${$tmp[1]}{$b} <=> ${$tmp[1]}{$a} } keys %{$tm +p[1]}; return \@tmp; # 0=site name, 1=hash ref, 2=total bytes for all t +ransmissions from site, 3=ranked dest names ref } __DATA__ 2000 lines, eg: gilligan.crew.hut lovey.howell.hut 4721 thurston.howell.hut lovey.howell.hut 4046 professor.hut ginger.girl.hut 5768 gilligan.crew.hut laser3.copyroom.hut 9352 gilligan.crew.hut maryann.girl.hut 1180 fileserver.copyroom.hut thurston.howell.hut 2548 skipper.crew.hut gilligan.crew.hut 1259
      This is more complex, but no longer than the solution in the book -- but the solution in the book does not create a permanent data structure either (goal was to just print ranked totals).
        # the next THREE are the same: # foreach my $line (@{$ref->[3]}) { print "\t$line ${$ref->[1]}{$line} +\n" } # foreach my $line (@{$$ref[3]}) { print "\t$line ${$$ref[1]}{$line}\n +" } foreach my $line (@{$$ref[3]}) { print "\t$line $all{$$ref[0]}{$line}\ +n" } }
        Notes ;)