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

I am starting to write perl code again for my job and have been just messing around with OO programming to refresh my memory (it's been almost 5 years since I've done any serious perl programming). Anyhow, I wrote a couple classes one of which when instantiated takes a reference to an array in my calling script.

The application simulates a shepherd locating 100 sheep in an an area 500x500. When the sheep objects are created, they are given random x,y coordinates and pushed on to the array. Then I instantiate my Field object and pass a reference to the sheep array. Then I instantiate my Shepherd object passing a reference to the Field object.

The shepherd then, in a loop looks around to find the closest sheep to him and goes and collects it. When he finds it, he removes that sheep from the original array. The issue is that I am using the splice command to remove the sheep, but the number of sheep before the splice is 100 and after is 99, but then on the next iteration of the loop where the shepherd locates the next closest sheep, he goes to remove the sheep from the original array which is showing 100 sheep again even though I removed (or thought I did) the sheep from the last iteration.

Maybe there's just a logic issue I'm missing here, but I can't figure out why my array seems to get reset to 100 each time and then after the splice is 99. It seems that the array is a copy and not the original. I checked the references and they appear to have the exact same address as the original array. Anyhow, it's probably simple, but I can't seem to get it. Here's the RemoveSheep method inside of my Shepherd object:
sub RemoveSheep { my $self = shift; my $obj = shift; # This is the sheep object my $field = ${$self->{field}}; my @sheep = @{$field->Sheep}; my $index = 0; my $found = undef; foreach my $sheep (@sheep) { if( $sheep == $obj ) { $found = $index; last; } $index++; } if( defined($found) ) { my $sheep = $sheep[$found]; print "Before Splice: " . @sheep . "\n"; splice (@sheep, $found, 1); print "After Splice: " . @sheep . "\n"; } }

And here is the calling script:
#!/usr/bin/perl -w use strict; use Sheep; use Shepherd; use Field; my $fieldWidth = 500; my $fieldHeight = 500; my @sheep = (); for(my $i = 0; $i < 100; ++$i) { my $randX = sprintf( "%d", rand($fieldWidth)); my $randY = sprintf( "%d", rand($fieldHeight)); my $sheep = new Sheep($randX, $randY); push(@sheep, $sheep); } print "Address of array is: " . \@sheep . "\n"; print "Shepherd now looking for " . @sheep . " sheep\n"; my $field = new Field( $fieldWidth, $fieldHeight, \@sheep); print "Shepherd starting at: 200:300\n"; my $shepherd = new Shepherd(\$field, 200, 300); $shepherd->FindSheep();

Replies are listed 'Best First'.
Re: Array Reference Issue
by wind (Priest) on Jul 15, 2007 at 03:45 UTC
    Yes, you are only splicing the sheep out of a copy of the array. To remove it from the original array, you must continue referring to it by reference.
    sub RemoveSheep { my $self = shift; my $obj = shift; # This is the sheep object my $field = ${$self->{field}}; my $sheep_ref = $field->Sheep; for my $index (0..$#$sheep_ref) { my $sheep = $sheep_ref->[$index]; if ( $sheep == $obj ) { print "Before Splice: " . @$sheep_ref . "\n"; splice @$sheep_ref, $index, 1; print "After Splice: " . @$sheep_ref . "\n"; last; } } }
    - Miller
      Yep. That did it. Works perfectly. Thanks for your help. I figured it was something simple.
Re: Array Reference Issue
by TOD (Friar) on Jul 15, 2007 at 03:43 UTC
    you dereference the array ref: my @sheep = @{$field->Sheep};, which means that from now on you are working on a copy of the original array. if you want the modified copy to be stored in the field again, insert a line like: $field->Sheep(\@sheep)
    --------------------------------
    masses are the opiate for religion.
Re: Array Reference Issue
by moritz (Cardinal) on Jul 15, 2007 at 15:27 UTC
    This has nothing to do with your original question, but you might consider to use a different data structure.

    If you store nothing else the "sheep" and "nosheep" state in your array, a sheep:nosheep ratio of 1:250 is rather small.

    Maybe you could just store a list of sheeps and their respective coordinates, and if you are looking for sheep you don't look in the positional table as you do it now, but in the list of sheep.

    Or you could use the techniques that are commonly used for sparse matrices, that is you have two arrays, @x and @y, and in each you store a doubly linked list of sheeps and their coordinates. If you want to know if there is a sheep at (4, 123) you walk the list $y[123] to see if there is a sheep at column index 4.