Edward Gargan
Edward Gargan

Reputation: 153

How can #? be used on a dereferenced array without first using @?

An array in perl is dereferenced like so,

my @array = @{$array_reference};

When trying to assign an array to a dereference without the '@', like,

my @array = {$array_reference};

Perl throws the error, 'Odd number of elements in anonymous hash at ./sand.pl line 22.' We can't assign it to an array variable becauase Perl is confused about the type.

So how can we perform...

my $lastindex = $#{$array_reference};

if Perl struggles to understand that '{$array_reference}' is an array type? It would make more sense to me if this looked like,

my $lastindex = $#@{$array_reference};

(despite looking much uglier).

Upvotes: 2

Views: 193

Answers (4)

ikegami
ikegami

Reputation: 386416

$array[$index]
@array[@indexes]
@array
$#array

is equivalent to

${ \@array }[$index]
@{ \@array }[@indexes]
@{ \@array }
$#{ \@array }

See the pattern? Wherever the NAME of an array isused, you can use a BLOCK that returns a reference to an array instead. That means you can use

${ $ref }[$index]
@{ $ref }[@indexes]
@{ $ref }
$#{ $ref }

This is illustrated in Perl Dereferencing Syntax.


Note that you can omit the curlies if the BLOCK contains nothing but a simple scalar.

$$ref[$index]
@$ref[@indexes]
@$ref
$#$ref

There's also an "arrow" syntax which is considered clearer.

$ref->[$index]
$ref->@[@indexes]
$ref->@*
$ref->$#*

Upvotes: 5

zdim
zdim

Reputation: 66924

The syntax is

my $lastindex = $#$array_reference;

which assigns to $lastindex the index of the last element of the anonymous array which reference is in the variable $array_reference.

The code

my @ary = { $ra };  # works but you get a warning

doesn't throw "an error" but rather a warning. In other words, you do get @ary with one element, a reference to an anonymous hash. However, a hash need have an even number of elements so you also get a warning that that isn't so.

Your last attempt dereferences the array with @{$array_reference} -- which returns a list, not an array variable. A "list" is a fleeting collection of scalars in memory (think of copying scalars on stack to go elsewhere); there is no notion of "index" for such a thing. For this reason a $#@{$ra} isn't even parsed as intended and is a syntax error.

The syntax $#ary works only with a variable @ary, and then there is the $#$arrayref syntax. You can in general write $#{$arrayref} since the curlies allow for an arbitrary expression that evaluates to an array reference but there is no reason for that since you do have a variable with an array reference.

I'd agree readily that much of this syntax takes some getting-used-to, to put it that way.

Upvotes: 2

melpomene
melpomene

Reputation: 85837

Perl is confused about the type

Perl struggles to understand that '{$array_reference}' is an array type

Well, it's not an array type. Perl doesn't "struggle"; you just have wrong expectations.

The general rule (as explained in perldoc perlreftut) is: You can always use a reference in curly braces in place of a variable name.

Thus:

@array           # a whole array
@{ $array_ref }  # same thing with a reference

$array[$i]           # an array element
${ $array_ref }[$i]  # same thing with a reference

$#array           # last index of an array
$#{ $array_ref }  # same thing with a reference

On the other hand, what's going on with

my @array = {$array_reference};

is that you're using the syntax for a hash reference constructor, { LIST }. The warning occurs because the list in question is supposed to have an even number of elements (for keys and values):

my $hash_ref = {
    key1 => 'value1',
    key2 => 'value2',
};

What you wrote is treated as

my @array = ({
    $array_reference => undef,
});

i.e. an array containing a single element, which is a reference to a hash containing a single key, which is a stringified reference (and whose value is undef).

The syntactic difference between a dereference and a hashref constructor is that a dereference starts with a sigil (such as $, @, or %) whereas a hashref constructor starts with just a bare {.


Technically speaking the { } in the dereference syntax form an actual block of code:

print ${
    print "one\n";  # yeah, I just put a statement in the middle of an expression
    print "two\n";
    ["three"]  # the last expression in this block is implicitly returned
               # (and dereferenced by the surrounding $ [0] construct outside)
}[0], "\n";

For (hopefully) obvious reasons, no one actually does this in real code.

Upvotes: 3

Schwern
Schwern

Reputation: 165318

tl;dr: It's $#{$array_reference} to match the syntax of $#array.


{} is overloaded with many meanings and that's just how Perl is.

Sometimes {} creates an anonymous hash. That's what {$array_reference} is doing, trying to make a hash where the key is the stringification of $array_reference, something like "ARRAY(0x7fb21e803280)" and there is no value. Because you're trying to create a hash with a key and no value you get an "odd number of elements" warning.

Sometimes {...} is a block like sub { ... } or if(...) { ... }, or do {...} and so on.

Sometimes it's a bare block like { local $/; ... }.

Sometimes it's indicating the key of a hash like $hash{key} or $hash->{key}.

Preceeded with certain sigils {} makes dereferencing explicit. While you can write $#$array_reference or @$array_reference sometimes you want to dereference something that isn't a simple scalar. For example, if you had a function that returned an array reference you could get its size in one line with $#{ get_array_reference() }. It's $#{$array_reference} to match the syntax of $#array.

$#{...} dereferences an array and gets the index. @{...} dereferences an array. %{...} dereferences a hash. ${...} dereferences a scalar. *{...} dereferences a glob.

You might find the section on Variable Names and Sigils in Modern Perl helpful to see the pattern better.

It would make more sense to me if this looked like...

There's a lot of things like that. Perl has been around since 1987. A lot of these design decisions were made decades ago. The code for deciding what {} means is particularly complex. That there is a distinction between an array and an array reference at all is a bit odd.

Upvotes: 6

Related Questions