Reference Question
caseydentinger
created: 2006-08-03 17:28:11
This foreach() will run several times, each time populating an array and putting a reference to it in a second array. When it finishes, however, all the references are to the same array (the last one to populate). I guess I could just split the array, pass it, then join it again, but that seems so bush league. Is there a better way?
my $doc_num;
my $tmp_doc_num;
my @doc_nums;
my @doc;

foreach( @doc_nums ) {
    @doc = ();
    $doc_num = substr( $_, 0, 7 );
    foreach( @raw ) {
        @fields = split( /,/, $_ );
        if( $fields[0] eq "AIP65" ) {
            $tmp_doc_num = substr( $fields[5], 0, 7 );;
        } else {
            $tmp_doc_num = substr( $fields[4], 0, 7 );
        }
        ( $tmp_doc_num eq $doc_num ) ? push( @doc, $_ ) : next;
    }
    push( @docs, \@doc );
}
Thanks in advance, Casey
Re: Reference Question
created: 2006-08-03 17:41:17

That's because you're reusing the same array each time through the loop. There's only one @doc - the one you declared outside the loop. You push a bunch of references to that array onto the @docs list, but since it's the same @doc, they'll all be the same.

Instead, try one of these idioms. My favourite is to just declare @doc inside your loop:

foreach( @doc_nums ) {
    my @doc;
This will create a new array each time. That allows the push to get a different reference. And since Perl does reference counting right, those arrays, though out of scope at the end of the loop, won't be cleared away because the @docs array still refers to them.

Second option: create a copy of the @doc array, and push that on to the list:

    push @docs, [ @doc ];
This will be a bit slower if @doc can be large, but is nice that it's explicitly obvious. That said, the first option is such a common idiom that it's pretty obvious after a while, too.

Hope that helps :-)

Re: Reference Question
created: 2006-08-03 17:41:24

Move my @doc inside the loop so you get a new instance of the doc array each time through. Consider:

use strict;
use warnings;

my @doc_nums = qw(1 2 3 4 5 6 7 8 9);
my @docs;

foreach( @doc_nums ) {
    my @doc;
    
    push @doc, int rand 10 for 1..3;
    push @docs, \@doc;
}

print "@$_\n" for @docs;

Prints:

9 5 8
9 5 7
2 2 1
3 0 4
8 6 4
6 1 7
6 5 5
8 6 9
9 9 7

or you could change the reference to a copy:

use strict;
use warnings;

my @doc_nums = qw(1 2 3 4 5 6 7 8 9);
my @docs;
    my @doc;

foreach( @doc_nums ) {
    @doc = ();
    push @doc, int rand 10 for 1..3;
    push @docs, [@doc];
}

print "@$_\n" for @docs;

DWIM is Perl's answer to Gödel
Re: Reference Question
created: 2006-08-03 17:43:11
push( @docs, \@doc );

You need push( @docs, [ @doc ] );. The reason is that if you use a backslash, you are taking a reference to the same variable, which contains differents things each time but the reference "points to" the same location in memory. Using square brackets, you create a new reference each time. That's the difference.

Update: To make things clearer, look at this program and its output:

use Data::Dumper;
my (@c, @d);

foreach (1..3) {
  @c[0..4] = (rand) x 2;
  push @d, \@c;
}
print Dumper \@d;

@d=();  ## reset @d
foreach (1..3) {
  @c[0..4] = (rand) x 2;
  push @d, [@c];
}
print Dumper \@d;
__END__
$VAR1 = [
          [
            '0.566846394018409',
            '0.566846394018409'
          ],
          $VAR1->[0],
          $VAR1->[0]
        ];
$VAR1 = [
          [
            '0.872105763616624',
            '0.872105763616624'
          ],
          [
            '0.0550789852978433',
            '0.0550789852978433'
          ],
          [
            '0.130973801235072',
            '0.130973801235072'
          ]
        ];

The first time, @d is populated with references to the same information ([mod://Data::Dumper] shows $VAR1->[0] instead of repeating it). The second example shows different numbers because [ ] create different references each time.

--
David Serrano

Re^2: Reference Question
created: 2006-08-03 17:48:48
Perfect! Thanks a ton. For my own edification, do the square brackets actually copy the array *and* make a reference to it in one fell swoop?
Re^3: Reference Question
created: 2006-08-03 17:54:24

The [] makes an anonymous copy of the array and in effect returns an reference to it. {} can be used in similar fashion to create an anonymous copy of a hash.


DWIM is Perl's answer to Gödel
Re^2: Reference Question
created: 2006-08-03 17:49:19

Actually you create a copy each time with the [], and push a reference to the copy.

In the original code the push was creating a new reference each time - to the same instance.


DWIM is Perl's answer to Gödel
Re: Reference Question
created: 2006-08-03 19:50:02

Any time you're not sure what's going on with a complex datastructure, I recommend you take a look at it using one (or more) of the techniques outlined here:


How can I visualize my complex data structure?

You might also wish to take a look at:


node 137108
node 69927

HTH,

planetscape

perlmonks.org content © perlmonks.org and caseydentinger, GrandFather, Hue-Bond, planetscape, Tanktalus

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03