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

Mr and Mrs Monks, I am new to OO perl and have stumbled into this problem. I have a fairly basic object, but I can’t get it to do what I want. When I try to create multiple instances I get a whole bunch of references to the same thing. I am not sure what I am doing wrong, most of my code came straight from the documentation. Here is the code for the object,
#!/usr/local/perl -w package VRTSAgent; use Carp; use Data::Dumper; ####################################### ## This is the ADT for an agent. ####################################### my %fields = ( agent =>undef, os =>%os, version =>@versions, ); sub new { my $class = shift; my $self = {%fields}; bless $self, $class; return $self; } sub agent { if ($_[1]) { $agent = $_[1]; } return $agent; } sub version { if ($_[2]) { $version[$_[1]]=$_[2]; } return $version[$_[1]]; } sub os { if ($_[3]) { $os{$_[1]}{$_[2]} = $_[3]; } return $os{$_[1]}{$_[2]}; } sub os_keys { return keys (%os); } sub os_key_each{ return keys ( %{$os{$_[1]}} ); } sub _dump_os { print Dumper(\%os); } sub make_Versions { my ($key, @keys, @each, %ver); @keys = &os_keys; foreach $key (@keys) { @each = keys ( %{$os{$key}} ); foreach $tmp (@each) { $ver{$tmp}=1; } } @versions = keys (%ver); @versions = sort @versions; } sub get_versions { return @versions; } sub DESTROY { }
and here is how I am using that code.
#!/usr/bin/perl -w use strict; use VRTSAgent; use Data::Dumper; my (@lines, $i, @tmp); my (@agents, @keys); my ($infile, $outfile); $infile = "matrix.csv"; $outfile = "out.csv"; open (INFILE, $infile); while (<INFILE>) { chomp; push @lines, $_; } close INFILE; my $j = -1 ; my $lastAgent = "something thats not an agent"; my (@osKey, $var, $var2, $var3, @tmp2, $agent); for ($i=2; $i < @lines; $i++) { @tmp = split /,/,$lines[$i]; if($lastAgent ne $tmp[3]){ $agents[++$j]= new VRTSAgent; $agents[$j]->new(); $lastAgent = $tmp[3]; $agents[$j]->agent($tmp[3]); print "New Agent == $tmp[3]\n"; } $agents[$j]->os($tmp[1]." ".$tmp[2],$tmp[4]." ".$tmp[5],$tmp[6]); @tmp = undef; } foreach $var (@agents) { $var->make_Versions; } my @ver; foreach $var (@agents) { $agent = $var->agent; @ver= $var->get_versions; foreach $var2 (@ver){ #print "$agent $var2 \n"; } }
What I want is to have a separate object for all of the different objects, instead I end up with one object and an array full of ?pointers/references? to the same one. If anyone has suggestions on how to make this do what I want or how to improve either piece of code I would be grateful. Here are some typical lines of input data,
SUN,Solaris 8 ,64-bit,Oracle ServerFree,9.0.1,64-bit,yes, IBM,AIX 4.3.3 ,64-bit,SAP,3.x ,64-bit,yes, IBM,AIX 4.3.3 ,64-bit,SAP,4.x,,yes, HP,HP-UX 11.11 ,32-bit,Sybase ,ASE 12.0,,yes,4.5A HP,HP-UX 11.11 ,64-bit,Sybase ,ASE 12.0,64-bit,yes,4.5A SGI,IRIX 6.5.10; 6.5.11; 6.5.12 ,64-bit,Sybase ,ASE 11.9.2,,yes,
Thanks again

Replies are listed 'Best First'.
Re: When I try to create different objects i end up getting the same one over and over...
by thelenm (Vicar) on Apr 26, 2002 at 15:31 UTC
    You may want to use strict; inside the code for VRTSAgent (after package VRTSAgent;). It looks like you're using several undeclared variables, and this may help you catch some errors.

    Another thing I notice right off the bat is these two lines:

    $agents[++$j]= new VRTSAgent; $agents[$j]->new();
    The first statement calls new(), so there's no need to call it again. In fact, when you do call it the second time, you're passing a VRTSAgent object as the first parameter rather than the name of a class, as your new() expects.
(jeffa) Re: When I try to create different objects i end up getting the same one over and over...
by jeffa (Bishop) on Apr 26, 2002 at 15:48 UTC
    If you have not already read the numerous tutorials on Perl OO, please do so: perltoot, perlboot, and our own Tutorials.

    You have quite a number of problems with your code, i'll attemp to rewrite some of your code, but not all. Try this out:

    use strict; use Data::Dumper; my @agents; open (FH,'matrix.csv'); while(<FH>) { chomp; push @agents, VRTSAgent->new(split(/\s*,\s*/,$_,6)); } close FH; print Dumper $_ for @agents; package VRTSAgent; use strict; use Carp; use Data::Dumper; sub new { my $class = shift; my $self = _init(@_); return bless $self, $class; } sub _init { my %init = ( agent => undef, os => [shift,shift], version => [@_], ); return \%init; } # here is how i would code _dump_os # note: if the client is going to call a method # then don't prefix that method with an underscore! # only private methods should be prefixed with underscores sub dump_os { my $self = shift; print Dumper($self->{os}); } # you could call it like so: $_->dump_os for @agents;
    I see no reason to store the data file in a transitory array, better to just make the object as you parse the line. So, each line is read and split on a comma with optional surrounding whitespace. The results are passed to the object's constructor, which passes them to it's internal _init() method.

    I totally got lazy with the _init() method - this is not good code (see Shift versus Sanity for discussions on why) , but i hope this gives you a new perspective on a hard part of this problem - turning each data piece into an attribute. Good luck, and let us know if you have any other problems in your endeavor.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      can you tell me more about what
      os => [shift,shift],
      is doing. its not the shift that I am confused about but rather what the data structure is now. In the original code that was a hash, but I am not sure what that syntax would make it. thanks Josh
        I thought about why you chose a hash and didn't see the benefit. I guess i need to see your requirements first before making that judgement, but again, my point was to show you how to get the most out of Perl - how to break away from C-style coding.

        The resulting data structure is an array reference. This may not be what you want, however. Check this out (assuming your data file):

        $_->dump_os for @agents; # yields: $VAR1 = [ 'SUN', 'Solaris 8' ]; $VAR1 = [ 'IBM', 'AIX 4.3.3' ]; # etc.
        Again, since i really do not know what your target is, i had to improvise. Now is the time when i wish i could be at your computer ... but alas ... if you want to continue this disussion, let's work via /msg and scratchpad's - no need to waste database space. ;)

        UPDATE:
        After some discussion via /msg's, i feel that storing the os's in a hash is not necessary at all. If you store them in an array reference as i have done, you can test for a particular OS by using grep. Here is an example:

        foreach (@agents) { $_->dump_os if grep /AIX/, @{$_->{os}}; }
        Of course, you could also grep a hash, but that seems a bit like using a square on a round hole to me. ;) Good luck!

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)