Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Multi-Dimensional Arrays and Array References

by Leudwinus (Scribe)
on Nov 16, 2020 at 15:02 UTC ( [id://11123691]=perlquestion: print w/replies, xml ) Need Help??

Leudwinus has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Monks,

I am hoping you can help me better understand array references and the use of multi-dimensional arrays in Perl. I need to be able to iterate through the rows and columns of a two-dimensional array as well as add columns to existing rows.

My first attempt was to use an array reference when constructing the array:

my $table = [[1, 2, 3], [4, 5, 6]]; # using reference

With this, I can iterate through all the elements:

for my $row (@{$table}) { for my $column (@{$row}) { print "$column\n"; } }

My two issues with this approach are that (1) I’m struggling with how to access an individual element of the table, such as the number 4

print "@{ $table }[1][0]\n"; # syntax error

and (2), how do I add additional elements so that $table ends up like this:

$table = [[1, 2, 3, 8], [4, 5, 6, 9]]

I also tried the following:

my @table2 = [[7, 8, 9], [10, 11, 12]]; # using array

which allows me to do this to iterate through the values:

for my $row (@table2) { for my $column (@{$row}) { print "@{$column}\n"; for my $element (@{$column}) { print "$element\n"; } } }

As well as this to print individual elements:

print "@{$table2[0][1]}[2]\n"; # prints 12

And add elements to the array:

push (@{$table2[0][1]}, 13); print "@{$table2[0][1]}\n"; # prints 10 11 12 13

But this method seems odd that I have to use 3 indices to access an element on a two-dimensional array.

Sorry for the long post but would appreciate some guidance on the best way to structure this array for idiomatic iteration and the ability to add elements after the array has been defined.

Gratias tibi ago
Leudwinus

Replies are listed 'Best First'.
Re: Multi-Dimensional Arrays and Array References
by Fletch (Bishop) on Nov 16, 2020 at 15:28 UTC

    You should (re-)read perlreftut and perllol while possibly having copies of Data::Dumper output of your samples' contents handy. That being said when you're referencing a single element of an array having a leading @ sigil is a good sign you're doing something wrong.

    You're also probably confusing yourself trying to directly compare an array reference in the scalar $table against an actual array @table2. When you initialize my @table2 = [ [7, 8, 9], ... ] what you're actually doing is putting a reference to an anonymous array into the 0th (first) element (i.e. you're setting $table2[0] to point to an arrayref) so that's where your extra level of indexen comes in.

    Edit: perldsc might also be useful for more examples. Also tweaked wording.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      Hi Fletch,

      You are correct that I am very confused about

      • the difference between
      • how to work with, and
      • how to compare

      arrays and array references but the links to the Perl documentation pages you provided look quite useful. Thank you for directing me that way.

      I really love how perldoc allows you to browse those pages directly on the command line too!

      Gratias tibi ago
      Leudwinus

Re: Multi-Dimensional Arrays and Array References
by choroba (Cardinal) on Nov 16, 2020 at 16:01 UTC
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; use Data::Dumper; my $ref = [[1, 2, 3], [4, 5, 6]]; # Print an element. say $ref->[1][1]; # Add a row. push @$ref, [7, 8, 9]; # Add a column. my @column = (3.5, 6.5, 9.5); push @{ $ref->[$_] }, $column[$_] for 0 .. $#column; # The same with an array. my @array = ([1, 2, 3], [4, 5, 6]); # Print an element. say $array[1][1]; # Add a row. push @array, [7, 8, 9]; # Add a column. my $column = \@column; push @{ $array[$_] }, $column->[$_] for 0 .. $#$column; Dumper($ref) eq Dumper(\@array) or die "Different";
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      Hi choroba,

      Thank you very much for your reply. If I may trouble you a bit more, I have a few more questions:

      1. When should I store my data in a reference such as

      my $ref = [[1, 2, 3], [4, 5, 6]];

      versus an array

      my @array = ([1, 2, 3], [4, 5, 6]);

      What is the difference?

      2. In both your first example using the reference, how come $ref->[$_] can’t be written as $ref[$_]?

      Gratias tibi ago
      Leudwinus

        When should I store my data in a reference ... versus an array

        In general consider using a reference when you plan to do 1 or more of these:

        • Passing your array to/from subroutines
        • Combining your array into a larger structure (see perldsc)
        • Using it as an object (unlikely, but not unheard of)
        • Having multiple references to the same data (obviously)

        See also the References tutorials subsection for other good info.


        🦛

        Regarding my second question above:

        Is it because $ref points to a reference of an array and therefore $ref->[2] would be the third element of that referenced array? Whereas $ref[2] would be the third element of the array @ref which perhaps doesn’t exist in this example?

Re: Multi-Dimensional Arrays and Array References
by johngg (Canon) on Nov 16, 2020 at 15:34 UTC
    my @table2 = [[7, 8, 9], [10, 11, 12]]; # using array

    To assign to an array you need parentheses, not square brackets.

    my @table2 = ([7, 8, 9], [10, 11, 12]);

    The following code might help you with accessing array references.

    johngg@abouriou:~/perl/Monks$ perl -Mstrict -Mwarnings -MData::Dumper +-E ' my $raTable = [ [ 1, 2, 3 ], [ 4, 5, 6 ], ]; say qq{\nNumber 4 is found at \$raTable->[ 1 ]->[ 0 ] - $raTable->[ 1 +]->[ 0 ]}; push @{ $raTable->[ 0 ] }, 8; push @{ $raTable->[ 1 ] }, 9; print Data::Dumper->Dumpxs( [ $raTable ], [ qw{ raTable } ] );' Number 4 is found at $raTable->[ 1 ]->[ 0 ] - 4 $raTable = [ [ 1, 2, 3, 8 ], [ 4, 5, 6, 9 ] ];

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Multi-Dimensional Arrays and Array References
by GrandFather (Saint) on Nov 16, 2020 at 20:16 UTC

    In Perl $, @ and % tell you what you get out of an expression, not what you are putting in. When you declare an array: my @table; the @ tells you this particular variable ("table") is an array. When you write my $value = $table[1]; the $ on each variable tells you you are dealing with scalar values. The table[1] expression accesses the second element of the array "table" and returns a scalar value - arrays only ever store scalars.

    Perl allows you to build complex structures by providing references to things. So an array can be an array of references to arrays - your case. The usual way to do that would be:

    my @table = ([1, 2, 3], [4, 5, 6]);

    An array of arrays. You can then iterate over all the elements by:

    for my $row (@table) { for my $cell (@$row) { printf "%4d", $cell; } print "\n"; }

    or, this being Perl you could:

    print "@$_\n" for @table;
    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Multi-Dimensional Arrays and Array References
by bliako (Monsignor) on Nov 16, 2020 at 19:39 UTC

    Start with an array my @arr = (1,2,3); Get a reference to it my $ref = \@arr; my $ref1 = \@arr; Note that ref and ref1 point to the same array, they are the same memory location: print $ref." and ".$ref1; Access elements: print $arr[1]; print $ref->[1];

    Or start with a (array) reference my $ref = [1,2,3]; "Dereference" it: my @arr = @{$ref}; print $arr[1];

    An array can hold scalars as well as references to other arrays or any other data structure or object. So in my @arr = (1,2,3, [7,8,9]); print $arr[1]; print $arr[3]->[1]; , the $arr[3] is a reference and its elements must be accessed using '->[]' as opposed to '[]'. Note that "->" can be used to access the internals (i.e. inside the memory space where the reference points to) of other types of references, e.g. the value from a hashref given the key or execute a subroutine inside an object. A "proper" 2D array, the way you want it, is just a 1D array whose 2 elements are references to other arrays: my @arr = ([1,2,3],[4,5,6]);

    And multi-dimensional arrays need not be rectangular or homogeneous as this shows: my @arr = (1,2,3, [7,8,9], File::Find::Node->new("/home/bliako") );

    If you have a C background these things are common and inbred. But if you don't, then you may wonder why things are not as simple as: @arr = ( (1,2,3), (4,5,6) ); This in actuality concatenates the two sub-arrays into a 1D array (as I understand it). I guess it would be too much dumping down for Perl to allow you to specify a 2D array this way and allow you (let alone "lead you") to abstract completely the hardware underneath and forget that RAM is 1D.

    This is also useful in order to demonstrate the above point: $ref = [1,2,3]; @arr = ($ref, $ref); $arr[0]->[1] = 66; print $ref->[1]; print $arr[1]->[1]; $ref->[1] = 33; print $arr[0]->[1];

    Or this: $ref = [1,2,3]; @arr = ($ref); $ref = [4,5,6]; push @arr, $ref; print $arr->[0]->[1]; print $arr->[1]->[1];

    References are memory addresses pointing to the real data structure or object. They are useful in that you can pass them around to subroutines without risking making copies to the whole data structure (re: https://www.effectiveperlprogramming.com/2010/05/use-scalar-references-to-pass-large-data-without-copying). Most importantly, you can have the subroutine modifying items inside the data structure. That's why I learned to love them. Strange love indeed! A program can be written without using a @arr = (...); at all, but only $ref = [...];

    Edit: "memory addresses pointing to the real data structure" maybe I should rephrase this to "they are the starting address of the memory where the real data structure resides". Also fixed ref1 and ref2.

    bw, bliako

Re: Multi-Dimensional Arrays and Array References
by Marshall (Canon) on Nov 16, 2020 at 23:32 UTC
    Here are some more examples for you...
    #!/usr/bin/perl use strict; use warnings; # simple 1D arrays: my @rowx = (1,2,3,4); my @rowy = qw (a b c); # A 2D array is an array of references to 1D arrays: # Note that Perl 2D arrays do not need to have the same # number of elements on each row! my @twoD = (\@rowx, \@rowy); foreach my $rowref (@twoD) { print "@$rowref\n"; } # prints # 1 2 3 4 # a b c # this can be a bit confusing... # In the first print, Perl knows that $twoD[0][1] means # an array element of @twoD, not be confused with # perhaps a simple scalar of the same name! # # In the second print, we get the value of $twoD, # a simple scalar my $twoD = "asdf"; print "$twoD[0][1]\n"; # prints 2 print "$twoD\n"; # prints asdf # In general, DO NOT use the same variable name for # two different sigils! Many things are "legal" in Perl # that you should not do!
    Update: You will notice that in my foreach loop, I used the name "rowref" instead of just "row". Perl wouldn't care. But naming the iteration variable like this helps me to remind myself to dereference that thing. YMMV.
      Note that Perl 2D arrays do not need to have the same number of elements on each row!

      nor even the same type of data:

      use strict; use warnings; package PrintMe; use overload '""' => sub {return $_[0]{str};}; sub new { return bless {str => $_[1]}, $_[0]; } package main; my @mixedTable = ( [1 .. 5], {a => 1, b => 2, wibble => 'plop'}, PrintMe->new("The quick brown fox") ); for my $row (@mixedTable) { if ('ARRAY' eq ref $row) { print "@$row\n"; } elsif ('HASH' eq ref $row) { print join ', ', map{"$_ => $row->{$_}"} sort keys %$row; print "\n"; } else { print "$row\n"; } }

      Prints:

      1 2 3 4 5 a => 1, b => 2, wibble => plop The quick brown fox
      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Multi-Dimensional Arrays and Array References
by perlfan (Vicar) on Nov 18, 2020 at 20:01 UTC
    One thing to note is that even if your first level variable is a hash or array (not a reference) creating any "nested" structure (for doing actual or interesting things) based on these will necessarily require references. Keep plugging away at it, the learning curve is steep here but persistence pays off wildly over time.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11123691]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (5)
As of 2024-04-24 10:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found