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

Hi Monks, I've got the following constructor:
sub new { my ($class, $fullpath, $date, $hostname) = @_; # Load log file in memory open(LOG_FILE, '<', $fullpath); @log_lines = <LOG_FILE>; close(LOG_FILE); # Define the object instance my $self = { log_lines => \@log_lines, fullpath => $fullpath, date => $date, hostname => $hostname }; bless($self, $class); return $self; }

After blessing the object, $self->{log_lines} is an ARRAY of ARRAYs. Why?

Thanks,
mc

Replies are listed 'Best First'.
Re: Blessing object changes array attribute in array of arrays?
by Corion (Patriarch) on Jul 06, 2005 at 12:56 UTC

    It must be in how you're inspecting the object, because $self->{log_lines} should be a reference to a single array. But if that's the actual code you're posting, then the code won't do what you want anyway, because you're using references to the global variable @log_lines, and so all your objects will reference the same array instead of getting a new array.

    You should be using the strict pragma and change your code to read

    ... local *LOG_FILE; open LOG_FILE, '<', $fullpath or die "Couldn't open logfile '$fullpath': $!"; my @log_lines = <LOG_FILE>; ...
      Thanks for your reply Corion,

      while I'm still trying to debug the 'ARRAY'->'ARRAY of ARRAYS' problem, I've started using the 'strict' pragma.

      Is a 'my' variable in the constructor always readable by the object INSTANCE?
      In our case, for example, what if in the constructor I set a reference to my @log_lines?

      mc

        You seem to be unaware of scoping. The best article I know about scoping is Coping With Scoping, which explains you all the stuff to know about global variables and lexical variables.

        For the problem at hand, yes, declaring @log_lines via my is exactly what you need, because then, each reference taken to @log_lines will be unique and point to its own incarnation of @log_lines, which seems to be what you want as $self->{log_lines} seems to be intended to be an instance variable and not a class/package variable.

        You also seem to have a misconception about how objects work in Perl, stemming from how you (don't) interpret scope. Variables declared via my are lexical variables that are only visible (under their name) within the closest set of enclosing curly braces ({}). An object usually is a (blessed) reference to a hash and you have to stuff all instance variables into that hash. The declaration in the constructor is not really related to that.

Re: Blessing object changes array attribute in array of arrays?
by rev_1318 (Chaplain) on Jul 06, 2005 at 14:23 UTC
    How did you come to the conclusion that it is a AoA? In the code you're showing, it is a reference to an array. But, since it is a package variable, it may be that you change the contents of @log_lines somewhere else in your package, and thus the reference to it changes as well...

    Paul

      I've extracted the fault code and I still get the same problem (AoA):

      #!/usr/bin/perl -w package McTest; use strict; sub new { my ($class, $path) = @_; print $path . "\n"; open LOG_FILE, '<', $path; my @lines = <LOG_FILE>; close LOG_FILE; my $self = { loglines => \@lines }; bless($self, $class); return $self; } my $mctest = McTest->new("access.log"); # referenced version foreach my $l($mctest->{loglines}) { print $l . "\n"; } # unreferenced version my @unref_array = $mctest->{loglines}; foreach my $l(@unref_array) { print $l . "\n"; }

      Any hints?
      Mc.
        foreach my $l ($mctest->{loglines}) { print $l . "\n"; }
        That should be
        foreach my $l(@{$mctest->{loglines}}) { print $l . "\n"; }
        $mctest->{loglines} is a reference to an array; @{$mctest->{loglines}} is that array.

        Dave.

        As dave_the_m points out, you're not dereferencing quite properly. Whenever I get output that doesn't match what I think it should, I always (always always) resort to Data::Dumper. Add something like this to your code:

        use Data::Dumper; print Dumper($mctest);
        You'll see that it's not an array of arrays very quickly this way. And that should lead you to suspect you're using it incorrectly. You may still have the question of how to dereference incorrectly, but at least it'll be the right question. :-)

        Data::Dumper has given me many a forehead-slapping "d'oh!" moment. Why? Because it has already had all the derefencing bugs worked out. It just works. My code generally isn't quite so reliable. :-)