Reputation: 153
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
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
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
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
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