user288609
user288609

Reputation: 13015

About accessing the Array of Arrays

I once read the following example about "array of arrays". AOA is a two dimensional array

The following code segment is claimed to print the whole thing with refs

for $aref ( @AoA ) {
  print "\t [ @$aref ],\n";
}

And the following code segment is claimed to print the whole thing with indices

for $i ( 0 .. $#AoA ) {
  print "\t [ @{$AoA[$i]} ],\n";
}

What's the $aref stand for here? How to understand the definition of @$aref and @{$AoA[$i]}? Thanks.

Upvotes: 1

Views: 1291

Answers (4)

TLP
TLP

Reputation: 67900

The whole mystery with multi-dimension structures in perl is quite easy to understand once you realize that there are only three types of variables to deal with. Scalars, arrays and hashes.

  • A scalar is a single value, it can contain just about anything, but only one at the time.
  • An array contains a number of scalar values, ordered by a fixed numerical index.
  • A hash contains scalar values, indexed by keys made of strings.

And all arrays, hashes or scalars act this way. Multi-dimension arrays are no different from single dimension.

This is also expressed very succinctly in perldata:

All data in Perl is a scalar, an array of scalars, or a hash of scalars. A scalar may contain one single value in any of three different flavors: a number, a string, or a reference. In general, conversion from one form to another is transparent. Although a scalar may not directly hold multiple values, it may contain a reference to an array or hash which in turn contains multiple values.

For example:

my @array = (1, 2, 3);

Here, $array[0] contains 1, $array[1] contains 2, etc. Just like you would expect.

my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ] );

Here, $array[0] contains an array reference. If you print it out, it will say something like ARRAY(0x398a84). Don't worry! That's still a scalar value. How do we know this? Because arrays can only contain scalar values.

When we do something like

for $aref ( @AoA ) {
  print $aref;  # prints ARRAY(0x398a84) or similar
}

It's no different from doing

for $number ( @array ) {
  print $number;
}

$aref and $number are scalar values. So far, so good. Take a moment and lock this knowledge down: Arrays can only contain scalar values.


Now, the next part is simply knowing how to deal with references. This is documented in perlref and perlreftut.

A reference is a scalar value. It's an address to a location in memory. This location contains some data. In order to access the actual data, we need to dereference the reference.

As a simple example:

my @data = (1, 2, 3);
my $aref = \@data;   # The backslash in front of the sigil creates a reference
print $aref;         # print something like ARRAY(0xa4b6a4)
print @$aref;        # prints 123

Adding a sigil in front of the reference tells perl to dereference the scalar value into the type of data the sigil represents. In this case, an array. If you choose the wrong sigil for the type of reference, perl will give an error such as:

Not a HASH reference

In the example above, we have a reference to a specific, named location. Both @$aref and @data access the same values. If we change a value in one, both are affected, because the address to the memory location is identical. Let's try it:

my @data  = (1, 2, 3);
my $aref  = \@data;
$$aref[1] = 'a';         # dereference to a scalar value by $ sigil
# $aref->[1] = 'a'       # does the same thing, a different way
print @data;             # prints 1a3
print @$aref;            # prints 1a3

We can also have anonymous data. If we were only interested in building an array of arrays, we'd have no interest in the @data, and could skip it by doing this:

my $aref = [ 1, 2, 3 ];

The brackets around the list of numbers create an anonymous array. $aref still contains the same type of data: A reference. But in this case, $aref is the only way we have of accessing the data contained at the memory location. Now, let's build some more scalar values like this:

my $aref1 = [ 1, 2, 3 ];
my $aref2 = [ 'a', 'b', 'c' ];
my $aref3 = [ 'x', 'y', 'z' ];

We now have three scalar variables that contain references to anonymous arrays. What if we put these in an array?

my @aoa = ($aref1, $aref2, $aref3);

If we'd want to access $aref1, we could do print @$aref1, but we could also do

print @{$aoa[0]};

In this case, we need to use the extended form of dereferencing: @{ ... }. Because perl does not like ambiguity, it requires us to distinguish between @{$aoa[0]} (take the reference in $aoa[0] and dereference as an array) and @{$aoa}[0] (take the reference in $aoa and dereference as an array, and take that arrays first value).

Above, we could have used @{$aref}, as it is identical to @$aref.

So, if we are only interested in building an array of arrays, we are not really interested in the $aref1 scalars either. So let's cut them out of the process:

my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ], [ 'x', 'y', 'z' ]);

Tada! That's an array of arrays.

Now, we can backtrack. To access the values inside this array, we can do

for my $scalar ( @aoa ) {
  print @$scalar;  # prints 123abcxyz
}

This time, I used a different variable name, just to make a point. This loop takes each value from @aoa -- which still is only a scalar value -- dereferences it as an array, and prints it.

Or we can access @aoa via its indexes

for my $i ( 0 .. $#aoa ) {
  print @{$aoa[$i]};
}

And that's all there is to it!

Upvotes: 1

ikegami
ikegami

Reputation: 385496

Perl doesn't have multidimensional arrays. One places arrays into other arrays to achieve the same result.

Well, almost. Arrays (and hashes) values are scalars, so one cannot place an array into another array. What one does instead of place a reference to an array instead.

In other words, "array of arrays" is short for "array of references to arrays". Each value of the @AoA is a reference to another array, given the "illusion" of a two-dimensional array.


The reference come from the use [ ] or equivalent. [ ] creates an anonymous array, then creates a reference to that array, then returns the reference. That's where the reference comes from.

Common ways of building an AoA:

my @AoA = (
   [ 'a', 'b', 'c' ],
   [ 'd', 'e', 'f' ],
);

my @AoA;
push @AoA, [ 'a', 'b', 'c' ];
push @AoA, [ 'd', 'e', 'f' ];

my @AoA;
$AoA[$y][$x] = $n;

Keep in mind that

$AoA[$y][$x] = $n;

is short for

$AoA[$y]->[$x] = $n;

and it's equivalent to the following thanks to autovivification:

( $AoA[$y] //= [] )->[$x] = $n;

Upvotes: 1

CanSpice
CanSpice

Reputation: 35790

An "array of arrays" isn't actually an array of arrays. It's more an array of array references. Each element in the base array is a reference to another array. Thus, when you want to cycle through the elements in the base array, you get back array references. These are what get assigned to $aref in the first loop. They are then de-referenced by pre-pending with the @ symbol, so @$aref is the array referenced by the $aref array reference.

Same sort of thing works for the second loop. $AoA[$i] is the $i-th element of the @AoA array, which is an array reference. De-referencing it by pre-pending it with the @ symbol (and adding {} for clarity, and possibly for precedence) means @{$AoA[$i]} is the array referenced by the $AoA[$i] array reference.

Upvotes: 1

Mat
Mat

Reputation: 206659

$aref stands for "array reference", i.e. a reference for an array.

my $my_aref = \@somearray;

You can make an array from an array reference with the following syntax:

@{$my_aref}

@{$my_aref} is @somearray. (It's not a copy, it really is the same array.)

In second example, $AoA[$i] is an array reference, and you dereference it with the same syntax: @{$AoA[$i]}.

See perlreftut for more explanations and examples.

Upvotes: 5

Related Questions