Reputation: 63
sub read_file {
open("person_file", "<person_data.txt");
my $class = shift;
my @people;
while (<person_file>) {
# entries are delimited by a | character
# format is
# firstname|lastname|streetaddress|town|state|zipcode
my $line = $_;
chomp($line);
my @broken_line = split(/\|/, $line);
print Dumper(@broken_line);
my $curr_person = new Person;
$curr_person->set_first_name($broken_line[0]);
$curr_person->set_last_name($broken_line[1]);
$curr_person->set_street_address($broken_line[2]);
$curr_person->set_town($broken_line[3]);
$curr_person->set_state($broken_line[4]);
$curr_person->set_zip_code($broken_line[5]);
print $curr_person->get_full_name(), "\n",
$curr_person->get_full_address(), "\n";
push(@people, $curr_person);
}
print "\n\n\n";
foreach my $person (@people) {
print $person->get_full_name(), "\n", $person->get_full_address(), "\n";
}
print Dumper(@people);
print "\n\n\n";
close("person_file");
return \@people;
}
Here is the output:
$VAR1 = 'K';
$VAR2 = 'M';
$VAR3 = '4th St';
$VAR4 = 'New York';
$VAR5 = 'NY';
$VAR6 = '10001';
K M
4th St
New York, NY 10001
$VAR1 = 'C';
$VAR2 = 'G';
$VAR3 = '3 Fifth Ave';
$VAR4 = 'New York';
$VAR5 = 'NY';
$VAR6 = '10003';
C G
3 Fifth Ave
New York, NY 10003
C G
3 Fifth Ave
New York, NY 10003
C G
3 Fifth Ave
New York, NY 10003
$VAR1 = bless( do{\(my $o = 'Person')}, 'Person' );
$VAR2 = bless( do{\(my $o = 'Person')}, 'Person' );
The first chunk of output occurs in the loop when I read the file. The second is in the second loop where I check the array just to see if all of the variables are proper, which they are not. So the problem I figured out was that $curr_person would not receive a new memory location even if it went out of scope or called as a new Person; and would share the memory location with $people[0] etc. making it so that all of the elements in people would be overwritten by whatever was in $curr_person.
Is there a way I can get $curr_person to get a new memory location each iteration of the loop?
Thanks
Person class:
package Person;
use strict;
use warnings;
my $first_name;
my $last_name;
my $street_address;
my $town;
my $state;
my $zip_code;
my $unique_id;
sub new
{
my $instance = shift;
bless \$instance, "Person";
}
This is my very first non-exercise (5 line) Perl project ever, and I am still trying to understand the syntax of OOP in Perl.
Upvotes: 0
Views: 385
Reputation: 1111
The answer you need is already in the comments: the Person constructor must be returning the same instance every time, and consequently every item in the @people
array is a pointer to that same instance, and when you update one you are updating all.
Show us the code for Person::new
.
Later:
Thanks for the code. Turns out I guessed wrong: it's not that your constructor is returning the same instance every time. The problem is with your data storage: every different Person instance is using the same set of variables (the my
declarations under package Person;
), so as you've already experienced, when you update one Person
, you've updated them all.
Find a tutorial or a textbook with a worked-out example of a class with get and set methods, and pay special attention to how each instance manages to keep its data (first name, last name, etc.) separate from all the others.
Upvotes: 1
Reputation: 57640
A method gets the invocant as first argument. The invocant is the object when called on an object:
$person->foo(); # $_[0] is Person object
or the class name (as a plain string) when called on that class:
Person->foo(); # $_[0] is "Person" string
The usual new
method looks like this:
# Usage: Person->new(first_name => "Fred", last_name => "Flintstone")
sub new {
my ($class, %args) = @_;
# process arguments
bless \%args => $class;
}
The first argument is the class name. We bless into that class (not neccessarily Person
!), so that our class can be inherited, and the new
method reused.
Inside the other methods, we use the fields in the object (which is just a special hashref) to store data – but not global or lexical variables!
sub first_name {
my $self = shift;
if (@_) { # if there are args
$self->{first_name} = shift;
}
return $self->{first_name};
}
et cetera. This is explained in perlootut which you should really read.
Because writing new
and accessors is repetitive and boring, people have come up with smart object frameworks that make working with objects more expressive. The most important is the Moose
framework. We could then write:
package Person;
use Moose; # this is now a Moose class
# declare attributes with `has`:
has first_name => (
is => 'rw', # this is now a read-write attribute
)
...
has street_address => ( is => 'rw');
has town => ( is => 'rw');
has state => ( is => 'rw');
has zip_code => ( is => 'rw');
...
# new is autogenerated!
# a normal method
sub get_full_address {
my ($self) = @_;
return sprintf "%s\n%s, %s %s",
$self->street_address, $self->town, $self->state, $self->zip_code;
}
Upvotes: 1