Reputation: 1064
Given:
my @list1 = ('a');
my @list2 = ('b');
my @list0 = ( \@list1, \@list2 );
then
my @listRef = $list0[1];
my @list = @$listRef; # works
but
my @list = @$($list0[1]); # gives an error message
I can't figure out why. What am I missing?
Upvotes: 0
Views: 142
Reputation: 66881
There is one simple de-referencing rule that covers this. Loosely put:
What follows the sigil need be the correct reference for it, or a block that evaluates to that.
A specific case from perlreftut
You can always use an array reference, in curly braces, in place of the name of an array.
In your case then, it should be
my @list = @{ $list0[1] };
(not index [2]
since your @list0
has two elements) Spaces are there only for readability.
The attempted @$($list0[2])
is a syntax error, first because the (
(following the $
) isn't allowed in an identifier (variable name), what presumably follows that $
.
A block {}
though would be allowed after the $
and would be evaluated, and must yield a scalar reference in this case, to be dereferenced by that $
in front of it; but then the first @
would be in error. That can then fixed as well but this gets messy if pushed, and wasn't meant to go that far. While the exact rules are (still) a little murky, see Identifier Parsing in perldata.
The @$listRef
earlier is correct syntax in general. But it refers to a scalar variable $listRef
(which must be an array reference since it's getting dereferenced into an array by the first @
), and there is no such a thing in the example -- you have an array variable @listRef
.
So with use strict;
in effect this, too, would fail to compile.
Dereferencing an arrayref to assign a new array is expensive as it has to copy all elements (and to construct the new array variable), while it's rarely needed (unless you actually want a copy). With the array reference on hand ($ar
) all that one may need is readily available
@$ar; # list of elements
$ar->[$index]; # specific element
@$ar[@indices]; # slice -- list of some elements, like @$ar[0,2..5,-1]
$ar->@[0,-1]; # slice, with new "postfix dereferencing" (stable at v5.24)
$#$ar; # last index in the anonymous array referred by $ar
See Slices in perldata and Postfix reference slicing in perlref
Upvotes: 4
Reputation: 132822
You have a lot going on there and multiple levels of inadvertent references, so let's go through it:
First, you start by making a list of two items, each of which is an array reference. You store that in an array:
my @list0 = ( \@list2, \@list2 );
Then you ask for the item with index 2, which is a single item, and store that in an array:
my @listRef = $list0[2];
However, there is no item with index 2 because Perl indexes from zero. The value in @listRef
in undefined. Not only that, but you've asked for a single item and stored it in an array instead of a scalar. That's probably not what you meant.
You say this following line works, but I don't think you know that because it won't give you the value you were expecting even if you didn't get an error. Something else is happening. You haven't declared or used a variable $listRef
, so Perl creates it for you and gives it the value undef
. When you try to dereference it, Perl uses "autovivification" to create the reference. This is the process where Perl helpfully creates a reference structure for you if you start with undef
:
my @list = @$listRef; # works
There is nothing in that array so @list
should be empty.
Fix that to get the last item, which has index of 1, and fix it so you are assigning the single value (the reference) to a scalar variable:
my $listRef = $list0[1];
Data::Dumper is handy here:
use Data::Dumper;
my @list2 = qw(a b c);
my @list0 = ( \@list2, \@list2 );
my $listRef = $list0[1];
print Dumper($listRef);
You get the output:
$VAR1 = [
'a',
'b',
'c'
];
Perl has some features that can catch these sorts of variable naming mistakes and will help you track down problems. Add these to the top of your program:
use strict;
use warnings;
For the rest, you might want to check out my book Intermediate Perl which explains all this reference stuff.
And, recent Perls have a new feature called postfix dereferencing that allows you to write dereferences from left to right:
my @items = ( \@list2, \@list2 );
my @items_of_last_ref = $items[1]->@*;
Upvotes: 2
Reputation: 385897
You need
@{ $list0[1] }
Whenever you can use the name of a variable, you can use a block that evaluates to a reference. That means the syntax for getting the elements of an array are
@NAME # If you have the name
@BLOCK # If you have a reference
That means that
my @array1 = 4..5;
my @array2 = @array1;
and
my $array1 = [ 4..5 ];
my @array2 = @{ $array1 }
are equivalent.
When the only thing in the block is a simple scalar ($NAME
or $BLOCK
), you can omit the curlies. That means that
@{ $array1 }
is equivalent to
@$array1
That's why @$listRef
works, and it's why @{ $list0[1] }
can't be simplified.
See Perl Dereferencing Syntax.
Upvotes: 2
Reputation: 21
The question is not complete and not clear on desired outcome.
OP tries to access an element $list0[2]
of array @list0
which does not exist -- array has elements with indexes 0 and 1.
Perhaps @listRef
should be $listRef
instead in the post.
Bellow is my vision of described problem
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my @list1 = qw/word1 word2 word3 word4/;
my @list2 = 1000..1004;
my @list0 = (\@list1, \@list2);
my $ref_array = $list0[0];
map{ say } @{$ref_array};
$ref_array = $list0[1];
map{ say } @{$ref_array};
say "Element: " . @{$ref_array}[2];
output
word1
word2
word3
word4
1000
1001
1002
1003
1004
Element: 1002
Upvotes: 1
Reputation: 5072
my @list = @$@listRef; # works
I doubt that works. That may not throw a syntax error but it sure as hell does not do what you think it does. For once
my @list0 = ( \@list2, \@list2 );
defines an array with 2 elements and you access
my @listRef = $list0[2];
the third element. So @listRef
is an array that contains one element which is undef
. The following code doesn't make sense either.
Unless the question is purely academic (answered by zdim already), I assume you want the second element of @list
into a separate array, I would write
my @list = @{ $list0[1] };
Upvotes: 1