-1

* UPDATED* for typos

Another PERL question.... I am trying to loop through a 2D array. I am positive about the size of one dimension but unsure on the second. The code snippet:

foreach my $value (@surfaces[1])
                {
                    my $sum = 0;
                    my $smallest = 9999;
                    my $limit_surface = 0;
                    for (my $i = 0; $i < 3; $i++)
                    {
                        $sum += $surfaces[$i][$counter];
                        if ($surfaces[$i][$counter] <= $smallest)
                        {
                            $smallest = $surfaces[$i][$counter];
                            $limit_surface = $subchannel_number[$i];
                        }
                    }
                    $counter++;
                    push(@avg_value,$sum/@rodsurface_number);
                    push(@limiting_schan,$limit_surface);
                    push(@limiting_value,$smallest);
               }

I am compiled but $value variable is failing to initialize.

TheCodeNovice
  • 750
  • 14
  • 35
  • @mpapec Thanks for spotting that. Can you assist with the array looping – TheCodeNovice Jun 30 '14 at 20:50
  • You should show how your variables are initialized, so people can actually run the code you show. It makes a big difference, trust me. – asjo Jun 30 '14 at 21:05

3 Answers3

3

Repeat after me:

Perl does not have multidimensional arrays
Perl does not have multidimensional arrays
Perl does not have multidimensional arrays

What Perl does have is have are arrays that contain references pointing to other arrays. You can emulate multidimensional arrays in Perl, but they are not true multidimensional arrays. For example:

my @array;
$array[0] = [ 1, 2, 3, 4, 5 ];
$array[1] = [ 1, 2, 3 ];
$array[2] = [ 1, 2 ];

I can talk about $array[0][1], and $array[2][1], but while $array[0][3] exists, $array[2][3] doesn't exist.

If you don't understand references, read the tutorial on references.

What you need to do is go through your array and then find out the size of each subarray and go through each of those. There's no guarantee that

  1. The reference contained in your primary array actually points to another array:
  2. That your sub-array contains only scalar data.

You can use the $# operator to find the size of your array. For example $#array is the number of items in your array. You an use ( 0..$#array ) to go through each item of your array, and this way, you have the index to play around with.

use strict;
use warnings;

my @array;
$array[0] = [ 1, 2, 3, 4, 5 ];
$array[1] = [ 1, 2, 3 ];
$array[2] = [ 1, 2, ];

#
# Here's my loop for the primary array.
#
for my $row ( 0..$#array ) {
    printf "Row %3d: ", $row ;
    #
    # My assumption is that this is another array that contains nothing
    # but scalar data...
    #
    my @columns = @{ $array[$row] };  # Dereferencing my array reference
    for my $column ( @columns ) {
        printf "%3d ", $column;
    }
    print "\n";
}

Note I did my @columns = @{ $array[$row] }; to convert my reference back into an array. This is an extra step. I could have simply done the dereferencing in my for loop and saved a step.

This prints out:

Row   0:   1   2   3   4   5 
Row   1:   1   2   3 
Row   2:   1   2 

I could put some safety checks in here. For example, I might want to verify the size of each row, and if one row doesn't match the other, complain:

my $row_size = $array[0];
for my $row ( 1..$#array ) {
    my @columns = @{ $array[$row] };
    if ( $#columns ne $array_size ) {
        die qq(This is not a 2D array. Not all rows are equal);
    }
}
David W.
  • 105,218
  • 39
  • 216
  • 337
  • `$#array` returns the last index of array, which is one less than the size of array. http://stackoverflow.com/questions/7406807/find-size-of-an-array-in-perl – stenlytw May 01 '16 at 19:24
  • This [list of lists](https://perldoc.perl.org/perllol.html) approach is definitely the right way to do it, but it should not be confused with the awful and arcane "multidimensional array emulation", as documented in [perldata](https://perldoc.perl.org/perldata.html#Multi-dimensional-array-emulation) and [perlvar](https://perldoc.perl.org/perlvar.html#%24%3b). Such "feature" exists for awk users. Including a comment here because this comes up on search engines for "multidimensional array emulation". – Possum Jan 09 '19 at 19:02
2

You do not describe your data structure, nor explain exactly what you want to do with it. This limits the advice that we can give to just the general variety.

If you're trying to iterate over an array of arrays, I would advise you to do it based off of element instead of index.

For example, below I have a 4 by 5 matrix of integers. I would like to find the average of these values. One way to do this is to simply iterate over each row and then column, and add up the values:

use strict;
use warnings;

my @AoA = (
    [11, 12, 13, 14, 15],
    [21, 22, 23, 24, 25],
    [31, 32, 33, 34, 35],
    [41, 42, 43, 44, 45],
);

my $sum = 0;
my $count = 0;

for my $row (@AoA) {
    for my $element (@$row) {    # <-- dereference the array ref
        $sum += $element;
        $count++;
    }
}

print "Average of Matrix is " . ($sum / $count) . "\n";

Outputs:

Average of Matrix is 28

For more information on complex data structures, check out: Perl Data Structures Cookbook

Miller
  • 34,962
  • 4
  • 39
  • 60
1

I've set up some dummy variables and changed a few things around. This compiles and produces the results I show below.

This might not answer your question, but should allow you to copy and paste the code, run it yourself, edit the input and see how the output compares to what you want.

use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

my @surfaces = ( ['1','2','3'],
            ['10','20','30'],
            ['100','200','400'],
            );

my @subchannel_number = ( ['1','2','3'],
            ['10','20','30'],
            ['100','200','400'],
            );  

my @rodsurface_number = (1 .. 10);
my $counter = 0;

my (@avg_value, @limiting_schan, @limiting_value);

foreach my $value ($surfaces[1]){
    my $sum = 0;
    my $smallest = 9999;
    my $limit_surface = 0;
        for (my $i = 0; $i < 3; $i++) {
            $sum += $surfaces[$i][$counter];
                if ($surfaces[$i][$counter] <= $smallest){
                    $smallest = $surfaces[$i][$counter];
                    $limit_surface = $subchannel_number[$i];
                }
        }
        $counter++;
        push(@avg_value,$sum/@rodsurface_number);
        push(@limiting_schan,$limit_surface);
        push(@limiting_value,$smallest);
}

print Dumper (\@avg_value, \@limiting_schan, \@limiting_value);

$VAR1 = [
          '11.1'
        ];
$VAR2 = [
          [
            '1',
            '2',
            '3'
          ]
        ];
$VAR3 = [
          1
        ];
fugu
  • 6,417
  • 5
  • 40
  • 75