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

This is not my exact script, but I wrote it in a similar format. Trying to create an array of arrays in one subroutine, and then returning it as a variable, to be used later in a second subroutine. When I test by printing indices from the arrays in the array of arrays, however, it does not seem to work (it says, for instance, "Use of uninitialized value $array1[0] in concatenation (.) or string"].

my @array1; my @array2; my @array3; my @array_array; @array_array = @{ &firstRoutine() }; &secondRoutine(\@array_array ); sub firstRoutine{ my $count = 0; while (<INPUT>){ my @line = split( /\s+/, $_ ); $array1[$count] = $line[0]; $array2[$count] = $line[1]; $array3[$count] = $line[2]; $count = $count + 1; } my @array_array; push( @array_array, @array1, @array2, @array3); return \@array_array; } sub secondRoutine { my (@array1, @array2, @array3) = { shift @_ }; print ("Test first value in array1: $array1[0]"); print ("Test third value in array2: $array2[2]"); print ("Test fifth value in array3: $array3[4]"); }

Replies are listed 'Best First'.
Re: Array of arrays
by tobyink (Canon) on Jul 18, 2013 at 22:15 UTC

    This:

    push( @array_array, @array1, @array2, @array3);

    should probably be:

    push( @array_array, \@array1, \@array2, \@array3);

    And this:

    my (@array1, @array2, @array3) = { shift @_ };

    should be:

    my @array1 = @{ shift(@_) }; my @array2 = @{ shift(@_) }; my @array3 = @{ shift(@_) };
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      I agree with tobyink, except that recreating the arrays when entering the second routine is not really optimal:

      sub secondRoutine { my @array1 = @{ shift(@_) }; my @array2 = @{ shift(@_) }; my @array3 = @{ shift(@_) }; print ("Test first value in array1: $array1[0]"); print ("Test third value in array2: $array2[2]"); print ("Test fifth value in array3: $array3[4]"); }

      I would usually prefer something like this:

      <c>sub secondRoutine { my ($array1_ref, $array2_ref, $array3_ref) = @_; print ("Test first value in array1: $$array1_ref[0]"); print ("Test third value in array2: $$array2_ref[2]"); print ("Test fifth value in array3: $$array3_ref[4]"); }

      This is matter open to discussion. Sometimes, I also create intermediary arrays from arrayrefs or hashref to make sure that I am really understanding my complicated data structure, but, here, it seems quite simple, no point of using memory to copy data structures.

      Update: fixed the copy-and-paste error seen by Monk::Thomas.

        (Copy&Paste error in your 'sub secondRoutine'? You define $array1_ref, $array2_ref, $array3_ref but use $$array1[0])
        sub secondRoutine { # there's nothing wrong here, but $aref (and $href) are shorter and # convey the same meaning #my ($array1_ref, $array2_ref, $array3_ref) = @_; my ($aref1, $aref2, $aref3) = @_; # - use references directly # - added newlines for clearer output # - brackets are unnecessary (just a style choice, no actual impact) #print ("Test first value in array1: $$array1[0]"); #print ("Test third value in array2: $$array2[2]"); #print ("Test fifth value in array3: $$array3[4]"); print "Test first value in array1: ", $aref1->[0], "\n"; print "Test third value in array2: ", $aref2->[2], "\n"; print "Test fifth value in array3: ", $aref3->[4], "\n"; }

      Thank you both for the responses. I still get an error (following the advice in the first response). When I do, for instance:

      print ("1st: $array1[0]"); print ("2nd: $array1[1]"); print ("3rd: $array1[2]"); print ("4th: $array1[3]"); print ("5th: $array1[4]"); print ("6th: $array1[5]"); print ("7th: $array1[6]"); print ("8th: $array1[7]"); print ("9th: $array1[8]"); print ("10th: $array1[9]"); print ("11th: $array1[10]"); print ("12th: $array1[11]");
      I get the following error:
      1st: ARRAY(0x7fd4bb012010) 2nd: ARRAY(0x7fd4bb02f768) 3rd: ARRAY(0x7fd4bb0120a0) 4th: ARRAY(0x7fd4bb012028) 5th: ARRAY(0x7fd4bb012070) 6th: ARRAY(0x7fd4bb02f648) 7th: ARRAY(0x7fd4bb045e80) 8th: ARRAY(0x7fd4bb0462e8) 9th: 10th: 11th: 12th : Use of uninitialized value $array1[8] in concatenation (.) or string Use of uninitialized value $array1[9] in concatenation (.) or string Use of uninitialized value $array1[10] in concatenation (.) or string Use of uninitialized value $array1[11] in concatenation (.) or string
      The strange thing is I have 9 arrays (array1, array2, ..., array9) in my array of arrays. But even when I am only calling array1 (which should have ~200 indices), it seems to be considering each array in the array of arrays as simply an index in the first of its arrays??

        The 'ARRAY(0x7fd4bb012010)' lines are telling you that you are attempting to print a Reference to an array. So, to print the 1st array, do this:

        my @first_array = @{$array1[0]}; print @first_array;

        Or maybe better, to print all of the arrays:

        foreach my $array_ref (@array1) { print @{$array_ref}; }

        The remaining errors for 'Use of uninitialized value' are telling you that those parts of the parent array are not defined, and therefore cannot be printed (you would have to see the internal of perlfunc's print for the explanation behind the concatenation part...).

        David

        SuzuBell,
        If you are still having problems fixing your data structure to properly print your values out, use Data::Dumper module to see how your data structure is formed.
        Check what you are printing again and compare it with the previous solutions given.

        If you tell me, I'll forget.
        If you show me, I'll remember.
        if you involve me, I'll understand.
        --- Author unknown to me

        You have been given several suggestions of correction. We don't know what you have implemented and what not. Please show us the full code so that we know what you have now. Or show the output of a Data::Dumper dump command. Better yet, show us both.

Re: Array of arrays
by kcott (Archbishop) on Jul 19, 2013 at 13:56 UTC

    G'day SuzuBell,

    Firstly, a few comments on your code (some of which may have already been addressed in other parts of this thread):

    • In general, you should not be prefixing your subroutine calls with an '&'. In fact, I would go so far as to say don't ever do it unless you know exactly why you're doing it. See perlsub for details.
    • You should not simply assume that some element of some array contains valid data. You need to test for this and code appropriate actions (which may include setting a default value or not processing that element). Not doing this is the likely cause of your "... uninitialised value ..." messages.
    • You appear to have a general lack of knowledge about references. That's fine: it's just something you need to learn — try the perlreftut tutorial (perlref has more complete details).
    • As you're working with complex data structures, you should look at the Perl Data Structures Cookbook. I'd recommend reading the initial sections up to CODE EXAMPLES; then, as a minimum for your current script, the ARRAYS OF ARRAYS section.
    • You're limiting yourself by hard-coding array variables with numbers: consider the number of lines of code you'd have to change if you needed an @array4. You can put (anonymous) references to each array into another array which can expand or contract as required.
    • You're doing lots of unnecessary processing with the continual referencing and dereferencing of your arrays. This will slow down your application and could potentially lead to memory issues. In the code I've posted below, see how I've eliminated all the additional processing associated with your two subroutine calls: secondRoutine(firstRoutine());

    The code below (which includes sample input and output) shows how to create, manipulate and access the type of array data you appear to be working with. It addresses all the issues I raised. I'll leave you to adapt it to your specific requirements.

    $ perl -Mstrict -Mwarnings -le ' use constant MAX_COLUMN_INDEX => 2; secondRoutine(firstRoutine()); sub firstRoutine{ my $matrix; while (<>) { my @fields = split; for (0 .. MAX_COLUMN_INDEX) { push @{$matrix->[$_]}, defined $fields[$_] ? $fields[$_] : "<undef>"; } } return $matrix; } sub secondRoutine { my $matrix = shift; for my $row (0 .. $#{$matrix->[0]}) { for my $col (0 .. MAX_COLUMN_INDEX) { print "Row $row Col $col = $matrix->[$col][$row]"; } } return; } ' q w e a s z Row 0 Col 0 = q Row 0 Col 1 = w Row 0 Col 2 = e Row 1 Col 0 = a Row 1 Col 1 = s Row 1 Col 2 = <undef> Row 2 Col 0 = z Row 2 Col 1 = <undef> Row 2 Col 2 = <undef>

    -- Ken