How can I create a two-dimensional array in Perl?

10,670

Solution 1

It doesn't seem you understand that $matrix only indicates @matrix when it is immediately followed by an array indexer: [ $slot ]. Otherwise, $matrix is a completely different variable from @matrix (and both different from %matrix as well). See perldata.

#!/usr/bin/perl
use English;

Don't! use English--that way!

This brings in $MATCH, $PREMATCH, and $POSTMATCH and incurs the dreaded $&, $`, $' penalty. You should wait until you're using an English variable and then just import that.

open FILE, "testset.txt" or die $!;

Two things: 1) use lexical file handles, and 2) use the three-argument open.

my @lines = <FILE>;

As long as I'm picking: Don't slurp big files. (Not the case here, but it's a good warning.)

my $size = scalar @lines;

my @matrix = (1 .. 32);
my $i = 0;
my $j = 0;
my @micro;

I see we're at the "PROFIT!!" stage here...

foreach ($matrix) {

You don't have a variable $matrix; you have a variable @matrix.

    foreach ($lines) {

The same thing is true with $lines.

        push @{ $micro[$matrix]}, $lines;
    }
}

Rewrite:

use strict;
use warnings;
use English qw<$OS_ERROR>; # $!
open( my $input, '<', 'testset.txt' ) or die $OS_ERROR;

# I'm going to assume space-delimited, since you don't show
my @matrix;
# while ( defined( $_ = <$input> ))...
while ( <$input> ) {
    chomp; # strip off the record separator
    # Load each slot of @matrix with a reference to an array filled with
    # the line split by spaces.
    push @matrix, [ split ]; # split = split( ' ', $_ )
}

Solution 2

If you are going to be doing quite a bit of math, you might consider PDL (the Perl Data Language). You can easily set up your matrix and before operations on it:

use 5.010;

use PDL;
use PDL::Matrix;

my @rows;
while( <DATA> ) {
    chomp;
    my @row = split /\s+/;
    push @rows, \@row;
    }

my $a = PDL::Matrix->pdl( \@rows );
say "Start ", $a;

$a->index2d( 1, 2 ) .= 999;
say "(1,2) to 999 ", $a;

$a++;
say "Increment all ", $a;

__DATA__
1 2 3
4 5 6
7 8 9
2 3 4

The output shows the matrix evolution:

Start 
[
 [1 2 3]
 [4 5 6]
 [7 8 9]
 [2 3 4]
]

(1,2) to 999 
[
 [  1   2   3]
 [  4   5 999]
 [  7   8   9]
 [  2   3   4]
]

Increment all 
[
 [   2    3    4]
 [   5    6 1000]
 [   8    9   10]
 [   3    4    5]
]

There's quite a bit of power to run arbitrary and complex operations on every member of the matrix just like I added 1 to every member. You completely skip the looping acrobatics.

Not only that, PDL does a lot of special stuff to make math really fast and to have a low memory footprint. Some of the stuff you want to do may already be implemented.

Solution 3

You probably need to chomp the values:

chomp( my @lines = <FILE> );

Solution 4

To clarify a tangential point to Axeman's answer:

See perldoc -f split:

A split on /\s+/ is like a split(' ') except that any leading whitespace produces a null first field. A split with no arguments really does a split(' ', $_) internally.

#!/usr/bin/perl

use YAML;

$_ = "\t1 2\n3\f4\r5\n";

print Dump { 'split'       => [ split       ] },
           { "split ' '"   => [ split ' '   ] },
           { 'split /\s+/' => [ split /\s+/ ] }
           ;

Output:

---
split:
  - 1
  - 2
  - 3
  - 4
  - 5
---
split ' ':
  - 1
  - 2
  - 3
  - 4
  - 5
---
split /\s+/:
  - ''
  - 1
  - 2
  - 3
  - 4
  - 5

Solution 5

I see the question is pretty old, but as the author has just edited the question, perhaps this is still of interest. Also the link to the data is dead, but since other answers use space as the separator, I will too.

This answer demonstrates Tie::Array::CSV which allows random access to a CSV (or other file parsable with Text::CSV).

#!/usr/bin/env perl

use strict;
use warnings;

use Tie::Array::CSV;

## put DATA into temporary file
## if not using DATA, put file name in $file
use File::Temp ();
my $file = File::Temp->new();
print $file <DATA>;
##


tie my @data, 'Tie::Array::CSV', $file, { 
  text_csv => {
    sep_char => " ",
  },
};

print $data[1][2];

__DATA__
1 2 3 4 5
6 7 8 9 1
2 3 4 5 6
Share:
10,670
Alos
Author by

Alos

I work at Harvard Medical School as a bioinformatician, I also started a company called Blue Elephant Jewelers. I enjoy writing code in R, perl, c#, java, php, and python. I am handy with Oracle 10g, and mysql. I have a degree in Bioinformatics and worked as a windows systems administrator for over 10 years.

Updated on September 06, 2022

Comments

  • Alos
    Alos over 1 year

    I am currently trying to pass a 32 by 48 matrix file to a multi-dimensional array in Perl. I am able to access all of the values, but I am having issues accessing a specific value.

    Here is a link to the data set: http://paste-it.net/public/x1d5301/

    Here is what I have for code right now.

    #!/usr/bin/perl
    
    open FILE, "testset.txt" or die $!;
    my @lines = <FILE>;
    
    my $size = scalar @lines;
    
    my @matrix = (1 .. 32);
    my $i = 0;
    my $j = 0;
    my @micro;
    
    foreach ($matrix)
    {
        foreach ($lines)
        {
            push @{$micro[$matrix]}, $lines;
        }
    }
    
  • silbana
    silbana over 13 years
    @Axeman FYI: split with no arguments is split ' ' not split /\s+/
  • Axeman
    Axeman over 13 years
    @Sinan: Not on redhat Perl 5.8.7 or Strawberry Perl 5.12.1:perl -MSmart::Comments $_ = '1 2 3 4 5'; my @a = split; ### @a ### @a: [ ### '1', ### '2', ### '3', ### '4', ### '5' ### ]
  • Axeman
    Axeman over 13 years
    @Sinan, well HTML compressed the spaces, but there are all sorts of spaces and tabs in there. :/
  • ysth
    ysth over 13 years
    @Axeman: can you explain what you mean? split with no arguments is definitely split ' ', not split /\s+/. The two differ in how leading spaces are treated.
  • silbana
    silbana over 13 years
    @Axeman See the CW answer I added to illustrate the difference.
  • Axeman
    Axeman over 13 years
    @Sinan, @ysth, ah my reading of perlfunc is a little rusty. I had forgotten "As a special case, specifying a PATTERN of space (' ' ) will split on white space just as split with no arguments does". So I thought that the significance of split( ' ' ) was it would only split on single-space characters--I only really use the shorthand of these functions in the toy code that I write on SO. You're right, @Sinan it does omit the initial zero-length string. My bad. And apparently that is simply to comply with awk.
  • Axeman
    Axeman over 13 years
    @Sinan: I guess my misguided point was that split() behaves more like split( /\s+/ ) than it does split( $char ) with $char = ' ' or $char = ','. The ' ' actually hides the full range of splitting behavior.
  • silbana
    silbana over 13 years
    @Axeman that's why I thought I should give you a heads up. Anyway, +1.