0

I have the following example exhibiting the problem I'm struggling to resolve. In the toy example, I have an array @actors with two levels. I also have an array of hashes @people which I am using to 'look up' properties of the people in @actors.

The output of the program should be:

blue, blue     cat, cat
red, red     dog, dog
blue, blue     cat, cat
red, red     dog, dog

but instead I'm getting:

blue, cat     cat, cat
red, dog     dog, dog
blue, cat     cat, cat
red, dog     dog, dog

That is, it seems that in setting $favanim[$i][$j] I seem to be also overwriting the value of $favcols[$i][$j]. I suspect that for some reason the fact that @actors is a 2-dimensional array means that assignments via = are as references rather than as values, though I don't know why or how to stop it. Please help!

The toy program is here: (I apologise if it can be simplified whilst still exhibiting the problem - it has taken me most of the afternoon to strip it down to this)

#!/usr/bin/perl -w

my @people = ();
$people[0]{'alternative full names for regexp'} = 'matthew smith|matt smith';
$people[1]{'alternative full names for regexp'} = 'david tennant|dave tennant';
$people[0]{'fav colour'} = 'red';
$people[1]{'fav colour'} = 'blue';
$people[0]{'fav animal'} = 'dog';
$people[1]{'fav animal'} = 'cat';

my @actors = ();
$actors[0][0] = 'David Tennant';
$actors[0][1] = 'Matt Smith';
$actors[1][0] = 'David Tennant';
$actors[1][1] = 'Matt Smith';
my @favcols = @actors;
my @favanim = @actors;

for ($i=0; $i<2; $i++) {
  for ($j=0; $j<2; $j++) {
    my @matching_people = grep{$actors[$i][$j] =~ m/^$_->{'alternative full names for regexp'}$/i} @people;
    $favcols[$i][$j] = $matching_people[0]{'fav colour'};
    $favanim[$i][$j] = $matching_people[0]{'fav animal'};
    print "$matching_people[0]{'fav colour'}, $favcols[$i][$j]     $matching_people[0]{'fav animal'}, $favanim[$i][$j]\n";
  }
}
Jack B
  • 123
  • 6

2 Answers2

2

Try using

@favcols = map { [@$_] } @actors;
@favanim = map { [@$_] } @actors;

Deep copy vs shallow copy.

Raghuveer
  • 1,413
  • 3
  • 23
  • 39
  • Thanks that works! How can I modify it to work in the case that `@actors` is a 3-dimensional array? – Jack B Jun 18 '15 at 18:30
  • 1
    Actually, now I know what to search for, these questions helped! http://stackoverflow.com/questions/4390030/in-perl-how-can-i-make-a-deep-copy-of-an-array http://stackoverflow.com/questions/388187/whats-the-best-way-to-make-a-deep-copy-of-a-data-structure-in-perl http://perldoc.perl.org/perlfaq4.html#How-do-I-print-out-or-copy-a-recursive-data-structure%3f – Jack B Jun 18 '15 at 19:57
1

The problem is that you are initialising @favcols and @favanim by copying the contents of @people, which contains two array references

That sets $favcol[0] and $favanim[0] to a reference to the same array, [ 'David Tennant', 'Matt Smith' ], so when you modify $favcols[$i][$j] and then $favanim[$i][$j] you are overwriting the same array element

I don't see any reason to initialise your arrays at all, and if you declare them as just

my (@favcols, @favanim);

then you will find that your program does what you expect

By the way, you must always use strict, and use warnings is preferable to -w on the command line

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • @JackB: Why do you want to copy the `@actors` array anyway, since you're overwriting the data? – Borodin Jun 18 '15 at 18:53
  • Thank you very much, for the explanation and the tip about declaring the variables without initialising them. In my actual script (not the toy example above) I wasn't overwriting all the values. But I have found a way around this now. So your answer was the best for me, though Raghuveer's answered the question I actually asked so I'll accept that one. (I would up arrow this but apparently I don't have enough reputation!) – Jack B Jun 18 '15 at 19:53