2

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).

melpomene
  • 84,125
  • 8
  • 85
  • 148
Edward Gargan
  • 153
  • 10

4 Answers4

6

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.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • 1
    Sometimes... Sometimes... Sometimes... Lovely, that's the whole spirit of Perl. TIMTOWDI – S.R.I Oct 10 '18 at 23:01
  • @S.R.I Sure. "Lovely". – Schwern Oct 10 '18 at 23:02
  • Christ -- I'm working through 'Beginning Perl' for this job I have coming up and I'm really not enjoying all this ambiguity. Thanks for the descriptive answer man. – Edward Gargan Oct 10 '18 at 23:15
  • 1
    Just to make things even more confusing, these days you can use `get_array_reference()->@*` to dereference an arrayref too. – Shawn Oct 10 '18 at 23:15
  • 2
    @EdwardGargan Give [Modern Perl](http://modernperlbooks.com/books/modern_perl_2016/index.html) a try. It provides a more coherent narrative of why things are the way they are in Perl, especially for understanding sigils. – Schwern Oct 10 '18 at 23:15
  • @Schwern I have that lined up next, just traipsing through this to get the basics down. – Edward Gargan Oct 10 '18 at 23:16
5
$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->$#*
ikegami
  • 367,544
  • 15
  • 269
  • 518
3

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.

melpomene
  • 84,125
  • 8
  • 85
  • 148
2

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.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Either work for me, but this doesn't answer my question. Not having to use {} is even more confusing for me! – Edward Gargan Oct 10 '18 at 22:58
  • @EdwardGargan (1) It shows you the syntax of "_how to perform_ [...]" -- how does that not answer your question? (2) Editing, with more discussion... – zdim Oct 10 '18 at 23:00
  • "how can we perform [...] _if Perl struggles to understand_", I was looking for an explanation for _why_ we can do something, now _how_, but thanks, I'll clarify this in the question. – Edward Gargan Oct 10 '18 at 23:03
  • @EdwardGargan Completed my answer -- does this help? – zdim Oct 10 '18 at 23:03
  • It does thanks, but I was looking for a bit more of an explanation on how Perl was handling the code fragments, rather than just why they were going wrong. Sorry for not making this clear in the Q. – Edward Gargan Oct 10 '18 at 23:20
  • 1
    @EdwardGargan Edited a little more, for clarity and completeness, and by now there are other answers which go into other aspects of it :) – zdim Oct 11 '18 at 00:28