lostbits
lostbits

Reputation: 1064

Why doesn't this deref of a reference work as a one-liner?

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

Answers (5)

zdim
zdim

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

brian d foy
brian d foy

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

ikegami
ikegami

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

Andrew
Andrew

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

Holli
Holli

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

Related Questions