1

Further to finding an answer to my earlier question Perl: slicing an array of hashes, 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";
3
  • 1
    You have both %Ds and @Ds. You assign to %Ds but @Ds is still empty. $Ds[1] selects a value from @Ds, which is undefined of course because there's nothing in there. @DDs becomes an array containing 1 scalar, which is undefined. $DDs[0] is that undefined scalar. Commented Oct 14, 2016 at 18:34
  • Naming variables with numeric suffixes (%dot1, %dot2) is a major code smell. Can you please explain what exactly you're trying to achieve? There's almost certainly a cleaner way to do it. Commented Oct 14, 2016 at 18:36
  • @ThisSuitIsBlackNot, the numeric suffixes are not meant to be used in the final version, just testing out this particular method. Trying to create a "family" of items with serveral properties (for testing, I chose points with x,y coordinates and a radius). These item hashes are grouped into a family array (two items in the test). Families belong to "groups". Trying to run tests on item properties against those in groups, some families of items are then allowed to be pushed into the group. . Commented Oct 14, 2016 at 18:46

1 Answer 1

6

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Very grateful for the detailed explanations @Schwern. I saw the {} mistake in the hash definitions and edited the question. Then I eventually notice that I had %Ds when I wanted to use an array - went through the code line by line and just couldn't see it till well after posting the question. I now see the @DDs=@{$Dx[1]} part. Thank you so much. Nested data structure chapter very helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.