user110084 user110084 - 1 month ago 7
Perl Question

Perl: array and hash

Further to finding an answer to my earlier question here, I am stuck again and unable to see what I have done wrong.

What I have is
Array( Array0(Hash0,Hash1),Array1(Hash0,Hash1),Array2(Hash0,Hash1)...)

use strict;
use warnings;

my @DDs=();
my @Ds=();
my %hsh=();

my %dot1 = ('x'=>1,'y'=>2,'r'=>3);
my %dot2 = ('x'=>4,'y'=>5,'r'=>6);
my %dot3 = ('x'=>7,'y'=>8,'r'=>9);
my %dot4 = ('x'=>1.1,'y'=>1.2,'r'=>1.3);
my %dot5 = ('x'=>2.1,'y'=>2.2,'r'=>2.3);
my %dot6 = ('x'=>3.1,'y'=>3.2,'r'=>3.3);
my %dot7 = ('x'=>4.1,'y'=>4.2,'r'=>4.3);
my %dot8 = ('x'=>5.1,'y'=>5.2,'r'=>5.3);

my @dotsA = (\%dot1,\%dot2);
my @dotsB = (\%dot3,\%dot4);
my @dotsC = (\%dot5,\%dot6);
my @dotsD = (\%dot7,\%dot8);

my %Ds = (\@dotsA,\@dotsB,\@dotsC,\@dotsD);

@DDs = $Ds[1]; #expect @dotsB with scalar of 2

###"Can't use an undefined value as HASH reference" error here
%hsh = %{$DDs[0]}; #expect %dot3

print scalar @DDs, "\n"; #expect 2 but has value of 1
print $hsh{'x'}, "\n";

Answer

Reference found where even-sized list expected at /Users/schwern/tmp/test.plx line 10.

Line 10 is this:

my %dot1 = {'x'=>1,'y'=>2,'r'=>3};

This is Perl's cryptic way of saying you fed a hash reference to a hash. Perl, unfortunately, distinguishes very strongly between things and references to that thing.

%dot1 is a hash. It takes a list and turns it into a hash. A list like ( x => 1, y => 2, r => 3). { x => 1, y => 2, r => 3 } creates a hash reference. That's a single thing, a scalar. It's like saying my %dot1 = (42). It doesn't make any sense.

  • %dot1 is a hash, it wants a list like (x => 1, y => 2)
  • $dot1 is a scalar, it can store a hash reference like { x => 1, y => 2 }.

my %Ds = (\@dotsA,\@dotsB,\@dotsC,\@dotsD);

A hash requires a key and a value, pairs. last_name => "Schwern". When you give it a bunch of array references like that, it will read them as key1, value1, key2, value2... but what is it using as the key? It's using the stringification of that reference, something like ARRAY(0x7fb721800468).

If you asked for $D{\@dotsA} you'll get back a reference to @dotsB. You will not be able to get @dotsA back, a Perl hash key is just a string, not a reference.

This isn't a very good way to store an array in a hash. I'm not sure what you're trying to accomplish, but you probably want to reference them by name.

# A hash of lists.
my %Ds = ( A => \@dotsA, B => \@dotsB, C => \@dotsC, D => \@dotsD );

# Get back a reference to @dotsA.
my $dotsA = $Ds{A};

But looking at the following code, @DDs = $Ds[1];, I think you meant to initialize @Ds instead of %Ds.

@Ds = (\@dotsA,\@dotsB,\@dotsC,\@dotsD);

And now the following works... sort of. More later.

@DDs = $Ds[1]; #expect @dotsB with scalar of 2

Unlike in PHP, hashes and arrays are totally different things. my @Ds and my %Ds declare totally different variables. It doesn't help that you access them both with $Ds. In Perl5, the sigil indicates what's going to get returned. $Ds[1] and $Ds{foo} both use $Ds because they're returning a scalar. @Ds[1,2] and @Ds{(foo, bar)} use @Ds because they're returning a list (known as a slice). Confusing, but that's how it works.


@DDs = $Ds[1]; #expect @dotsB with scalar of 2

You're not getting @dotsB, you're getting a reference to @dotsB. All complex data structures in Perl store references, not the actual value. This is like $DDs[0] = \@dotsB. If you want to get the actual value you have to dereference it.

@DDs = @{$Ds[1]};  # Now @DDs has a copy of @dotsB

And finally it works.

#!/usr/bin/perl
use strict;
use warnings;
use v5.10;    # for say()

my %dot1 = ('x'=>1,'y'=>2,'r'=>3);
my %dot2 = ('x'=>4,'y'=>5,'r'=>6);
my %dot3 = ('x'=>7,'y'=>8,'r'=>9);
my %dot4 = ('x'=>1.1,'y'=>1.2,'r'=>1.3);
my %dot5 = ('x'=>2.1,'y'=>2.2,'r'=>2.3);
my %dot6 = ('x'=>3.1,'y'=>3.2,'r'=>3.3);
my %dot7 = ('x'=>4.1,'y'=>4.2,'r'=>4.3);
my %dot8 = ('x'=>5.1,'y'=>5.2,'r'=>5.3);

my @dotsA = (\%dot1,\%dot2);
my @dotsB = (\%dot3,\%dot4);
my @dotsC = (\%dot5,\%dot6);
my @dotsD = (\%dot7,\%dot8);

my @Ds = (\@dotsA,\@dotsB,\@dotsC,\@dotsD);
my @DDs = @{$Ds[1]}; #expect @dotsB

my %hsh = %{$DDs[0]}; #expect %dot3

say scalar @DDs; #expect 2
say $hsh{'x'};

I would also advise that you get comfortable working directly with references since that's what complex data structures are: references. Converting back and forth from references to values is confusing. Working with references is one less thing to convert in your code, in your head, and less copying done in your program.

#!/usr/bin/perl
use strict;
use warnings;
use v5.10;    # for say()

my $dot1 = {'x'=>1,'y'=>2,'r'=>3};
my $dot2 = {'x'=>4,'y'=>5,'r'=>6};
my $dot3 = {'x'=>7,'y'=>8,'r'=>9};
my $dot4 = {'x'=>1.1,'y'=>1.2,'r'=>1.3};
my $dot5 = {'x'=>2.1,'y'=>2.2,'r'=>2.3};
my $dot6 = {'x'=>3.1,'y'=>3.2,'r'=>3.3};
my $dot7 = {'x'=>4.1,'y'=>4.2,'r'=>4.3};
my $dot8 = {'x'=>5.1,'y'=>5.2,'r'=>5.3};

my $dotsA = [$dot1,$dot2];
my $dotsB = [$dot3,$dot4];
my $dotsC = [$dot5,$dot6];
my $dotsD = [$dot7,$dot8];

my $Ds = [$dotsA,$dotsB,$dotsC,$dotsD];
my $DDs = $Ds->[1]; #expect $dotsB

my $hsh = $DDs->[0]; #expect $dot3

say scalar @$DDs; #expect 2
say $hsh->{'x'};

You should review perlreftut and the Nested Data Structures chapter of Modern Perl.

Comments