Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do references in Perl work

Tags:

perl

Can anyone explain the push statement in the following Perl code to me? I know how push in perl works but I can't understand what the first argument in following push command represents. I am trying to interpret someone's script. I tried to print "@a\n"; but it only printed ARRAY(0x9aa370) which makes me think that the push is not doing anything. Any help is appreciated. Thanks!

my @a = (); 
my $b = 10;
my $c = 'a';
push(@{$a[$b]}, $c);
like image 305
user166918 Avatar asked Aug 24 '17 22:08

user166918


2 Answers

Let's break it down.

The @{...} is understood from "Using References" in perlref

Anywhere you'd put an identifier (or chain of identifiers) as part of a variable or subroutine name, you can replace the identifier with a BLOCK returning a reference of the correct type.

So what is inside { ... } block had better work out to an array reference. You have $a[$b] there, an element of @a at index $b, so that element must be an arrayref.

Then @{...} dereferences it and pushes a new element $c to it. Altogether, $c is copied into a (sole) element of an anonymous array whose reference is at index $b of the array @a.

And a crucial part: as there is in fact no arrayref there, the autovivification kicks in and it is created. Since there are no elements at indices preceding $b they are created as well, with value undef.

Now please work through

  • tutorial perlreftut and

  • data-structures cookbook perldsc

while using perlref linked in the beginning for a full reference.


With complex data structures it is useful to be able to see them, and there are tools for that. A most often used one is the core Data::Dumper, and here is an example with Data::Dump

perl -MData::Dump=dd -wE'@ary = (1); push @{$ary[3]}, "ah"; dd \@ary'

with output

[1, undef, undef, ["ah"]]

where [] inside indicate an arrayref, with its sole element being the string ah.


More precisely, an undef scalar is dereferenced and since this happens in an lvalue context the autovivification goes. Thanks to ikegami for a comment. See for instance this post with its links.

like image 102
zdim Avatar answered Sep 30 '22 13:09

zdim


Let's start with the following two assertions:

  • @a starts out as an empty array with no elements.
  • $b is assigned the value of 10.

Now look at this construct:

@{$a[$b]}

To understand we can start in the middle: $a[$b] indexes element 10 of the array @a.

Now we can work outward from there: @{...} treats its contents as a reference to an array. So @{$a[$b]} treats the content of element 10 of the array @a as a reference to an anonymous array. That is to say, the scalar value contained in $a[10] is an array reference.

Now layer in the push:

push @{$a[$b]}, $c;

Into the anonymous array referenced in element 10 of @a you are pushing the value of $c, which is the character "a". You could access that element like this:

my $value = $a[10]->[0]; # character 'a'

Or shorthand,

my $value = $a[10][0];

If you pushed another value into @{$a[10]} then you would access it at:

my $other_value = $a[10][1];

But what about $a[0] through $a[9]? You're only pushing a value into $a[$b], which is $a[10]. Perl automatically extends the array to accommodate that 11th element ($a[10]), but leaves the value in $a[0] through $a[9] as undef. You mentioned that you tried this:

print "@a\n";

Interpolating an array into a string causes its elements to be printed with a space between each one. So you didn't see this:

ARRAY(0xa6f328)

You saw this:

ARRAY(0xa6f328)

...because there were ten spaces before the 11th element which contains an array reference.

If you were running your script with use warnings at the top, you would have seen this instead:

Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
          ARRAY(0xa6f328)

...or something quite similar.

Your structure currently looks like this:

@a = (undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,['a'])

If you ever want to really get a look at what a data structure looks like, rather than using a simple print, do something like this:

use Data::Dumper;
print Dumper \@a;
like image 25
DavidO Avatar answered Sep 30 '22 11:09

DavidO