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

Hey Everyone

I'm having the strangest problem. Here's the deal. I'm writing a decently ugly script that parses a spreadsheet according to options specified by the user in a config file. I have several subroutines that perform data manipulations according to the contents of the config file.

One subroutine (1) takes some data, pushes its values into a 2D array, does some manipulations to it, prepends a value to each element, and merges it back with the main array. This piece of code has been well tested, and works quite well.

Another subroutine (2) selects certain columns from the main data, pushes their values into a DIFFERENT 2D array, and merges this newly-selected array back in to the main data.

The only thing shared between these two subroutines is the main array of data (2D array). It's passed to (1), formatted, and passed back to the main loop, then passed to (2). Now here's the REALLY strange part. As I'm taking values from the main array and putting them in my temporary array, I'm checking them with print statements. The print statements don't show what I push into the temporary arry, but instead show the values that were pushed into a DIFFERENT temporary array in a DIFFERENT subroutine! Here's a code snippet to illustrate:

@data: the main block of data from the SS
@holder: the temporary array set up for manipulation purposes
$sheet, $iC, $index: Numerical indices for accessing my nested arrays.

print "Pushing ".@{@{@data[$sheet]}[$iC]}[$index]." onto holder[$sheet +][$iC]\n\n\n"; push(@{@{@holder[$sheet]}[$iC]},@{@{@data[$sheet]}[$iC]}[$index]); print "holder[$sheet][$iC] = ".@{@holder[$sheet]}[$iC]."\n";

This produces the following:

Pushing 1-Jun-97 onto holder[0][3] holder[0][3] = TTY9.3
TTY9.3 is one of the values generated by subroutine (1). It was originally put into an array similar to @holder, but it was called @tempdata, and was in a completely different subroutine. Now it would seem that this shouldn't be happening, given that the two temporary arrays are of different names and scopes. The only thing that they have in common is that the nesting levels are the same (ie, @tempdata[$sheet][$iC] held TTY9.3 while @holder[$sheet][$iC] SHOULD hold a date), and as such they both get casted to full arrays through the @{} operator. Is it something strange like a mixup in whatever temporary space perl uses to cast nested arrays, or something REALLY basic that a noob like me is missing?

Can anyone help at all with this? Even a clue as to what may be happening? I can provide any additional info you need, save for the entire script (it's over 2000 lines and I'm quite new to perl so it's UGLY).

ActivePerl 5.8.0, Win2k

Replies are listed 'Best First'.
Re: Strange problem with nested arrays and scoping.
by Zaxo (Archbishop) on Dec 02, 2003 at 20:28 UTC

    You've got yourself into a nasty tangle with reference and sigil syntax. See References quick reference by tye for a review. I can't tell whether you are trying to push a single element or an array, but push( @{$holder[$sheet][$iC]}, $data[$sheet][$iC][$index]); seems a likely interpretation of what you want.

    After Compline,
    Zaxo

      Very true. Thank you for the tip. I've cleaned up my code (throughout the entire script), and it's now considerably less gory to read.

      However, the problem persists :(

      print "Pushing ".$data[$sheet][$iC][$index]." onto holder[$sheet][$iC] +\n\n\n"; push(@{$holder[$sheet][$iC]}, $data[$sheet][$iC][$index]); print "holder[$sheet][$iC] = ".$holder[$sheet][$iC]."\n";
      Yields
      Pushing 1-Jun-97 onto holder[0][3] holder[0][3] = TTY9.3

      Other notes:
      @holder is initalized with "my @holder". $holder[$sheet] is initialized with "$holder[$sheet] = ('');" (so that I can reference it in array context and push values.

      Anyone know what else may be happening?

        Erm, $holder[$sheet][$iC] ought to print as something like "ARRAY(0x8106164)" since the push makes it an array reference. Is there more to this than you've shown?

        After Compline,
        Zaxo

Re: Strange problem with nested arrays and scoping.
by Roy Johnson (Monsignor) on Dec 02, 2003 at 20:36 UTC
    There are probably some references assigned somewhere in the code -- perhaps a reference to the tempdata array is returned by the subroutine that creates it? What is the last line of that subroutine -- or include the whole thing, if it's not especially long.

    The notation for array indexing is pretty bletcherous. They're taking scalar slices of arrays instead of indexing a scalar element. It would be much nicer to see $data[$sheet][$iC][$index] than ${${$data[$sheet]}[$iC]}[$index], which is at least sigil-correct for @{@{@data[$sheet]}[$iC]}[$index].


    The PerlMonk tr/// Advocate
      Ahhhh...that did it. In the first subroutine, I had @{$data[$sheet][$iC]} = @{$tempdata[$sheet][$iC]}. I changed this to $data[$sheet][$iC] = \@{$tempdata[$sheet][$iC]} and screwed around with some references in the second subroutine, and it's correctly accumulating my value list. Thanks a lot everyone! I'm on my way again, and with a new knowledge of how (and how NOT) to reference arrays :) You live and learn.
Re: Strange problem with nested arrays and scoping.
by mpeppler (Vicar) on Dec 02, 2003 at 20:39 UTC
    The first thing that I'm seeing looking at your code snippets is that you confuse array elements and array slices. You write:
    push(@{@{@holder[$sheet]}[$iC]},@{@{@data[$sheet]}[$iC]}[$index]);
    and I'm almost certain that you mean:
    push(@{${$holder[$sheet]}[$iC]},${${$data[$sheet]}[$iC]}[$index]);
    To simplify - to access an array element you use:
    $array[254] = 'foo';
    The @array[...] notation is used to access a slice, for example @foo = @array[2 .. 4]; which would copy the data from $array[2], $array[3] and $array[4] to $foo[0], $foo[1], $foo[2].

    Fix your code to use $ instead of @ correctly, and see if that fixes your problem.

    Michael