http://qs321.pair.com?node_id=761672

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

Greetings, fellow monks;

I have a sub that needs to print out an array of arrays (or array of arrayrefs, depending on the version of my code), but for some reason foreach doesn't seem to select anything from the first array.

An example would perhaps clarify the problem more:

sub convFieldToXML($) { my $fldref = shift; print "fldref=\n"; dump($fldref); print "fldref[TXT]=\n"; dump(@$fldref[TXT]); if (ref(@$fldref[TXT]) eq 'ARRAY') { foreach my $tmp (@$fldref[TXT]) { print "tmp=\n"; dump($tmp); } } else { # snip --8<--; } }

Short fragment of output:

fldref= [ 500, 0, 0, [ ["\$a", "Tarina Babarista, pienest{ elefantista"], ["\$s", "(26'34); orkestroitu"], ["\$d", "Poulenc, Francis"], ["\$g", "Brunhoff, Jean de "], ["\$h", "P|ysti, Lasse"], ["\$j", "Fran*8aix, Jean"], ], ] fldref[TXT]= [ ["\$a", "Tarina Babarista, pienest{ elefantista"], ["\$s", "(26'34); orkestroitu"], ["\$d", "Poulenc, Francis"], ["\$g", "Brunhoff, Jean de "], ["\$h", "P|ysti, Lasse"], ["\$j", "Fran*8aix, Jean"], ] tmp= [ ["\$a", "Tarina Babarista, pienest{ elefantista"], ["\$s", "(26'34); orkestroitu"], ["\$d", "Poulenc, Francis"], ["\$g", "Brunhoff, Jean de "], ["\$h", "P|ysti, Lasse"], ["\$j", "Fran*8aix, Jean"], ]

Or also (with refs):

fldref= [ 500, 0, 0, [ \["\$a", "Tarina Babarista, pienest{ elefantista"], \["\$s", "(26'34); orkestroitu"], \["\$d", "Poulenc, Francis"], \["\$g", "Brunhoff, Jean de "], \["\$h", "P|ysti, Lasse"], \["\$j", "Fran*8aix, Jean"], ], ] fldref[TXT]= [ \["\$a", "Tarina Babarista, pienest{ elefantista"], \["\$s", "(26'34); orkestroitu"], \["\$d", "Poulenc, Francis"], \["\$g", "Brunhoff, Jean de "], \["\$h", "P|ysti, Lasse"], \["\$j", "Fran*8aix, Jean"], ] tmp= [ \["\$a", "Tarina Babarista, pienest{ elefantista"], \["\$s", "(26'34); orkestroitu"], \["\$d", "Poulenc, Francis"], \["\$g", "Brunhoff, Jean de "], \["\$h", "P|ysti, Lasse"], \["\$j", "Fran*8aix, Jean"], ]

Note that @$fldref[TXT] has the same output as $tmp, despite the foreach(). I expected $tmp to contain each array from the second level of array-of-arrays (or array-of-refs). Instead, foreach doesn't seem to select anything.

Help?

Replies are listed 'Best First'.
Re: foreach doesn't select array element
by ELISHEVA (Prior) on May 04, 2009 at 10:04 UTC

    I would expect dump(@$fldref[TXT]); and dump($tmp) to print out the same output.

    dump(@$fldref[TXT]) is equivalent to dump($fldref[TXT][0], $fldref[TXT][1], ...). But since there is only one array element in $fldref[TXT] we get:

    dump(@$fldref[TXT]) equivalent to dump($fldref[TXT][0]) which is equivalent to dump($tmp) - hence the same output.

    Best, beth

      But shouldn't the foreach() have selected the first array element? If I have a simple array (anna, beth, christie, denise) and pass this @array to foreach, it returns the values. Well, @$fildref3 is a simple array, so when I pass it to foreach(), I expect it to return the members of this array, which is either an array of scalar pairs or the refs to these (in code example 2). I fail to understand why this doesn't work (simulated dump output):
      \@arr = [ 1, 2, 3, [ anna, beth, christie, denise ], ] foreach my girl (@$arr[3]) { print "$girl\n"; }

      This is what I try to do. Dump doesn't show:

      \@arr = [ 1, 2, 3, \[ anna, beth, christie, denise ], ]

      (Note the ref marker in front of the girl array.)

      If I understand correctly, the @{} is needed to deref the array ref. But Dump shows me it's an ordinary array, like in my first simple array example in this post.

      Thanks for helping out...

        Your confusion may be coming from the order in which @ and [3] are being handled. @ first converts $fldref from an array reference into an array. Then it lets you use normal array element syntax, @$fldref[3], to access its elements. In other words, @$fldref[3] is equivalent to $fldref->[3].

        You might be able to see this more clearly if we replace $fldref->[3] with a variable, like this:

        my $aVars= [ ["\$a", "Tarina Babarista, pienest{ elefantista"], ["\$s", "(26'34); orkestroitu"], ["\$d", "Poulenc, Francis"], ["\$g", "Brunhoff, Jean de "], ["\$h", "P|ysti, Lasse"], ["\$j", "Fran*8aix, Jean"], ]; my $fldref= [ 500, 0, 0, $aVars ];

        $fldref->[3] is $aVars, a reference to an array and not the dereferenced array, @$aVars. So when you loop on foreach (@$fldref[3]) you are really looping on foreach($fldref->[3]) which is just a loop over the single element $aVars. That is why you dump only once inside the foreach loop and see the $aVars array as a whole when you dump it rather than the individual elements of $aVars, each dumped separately.

        If you want to loop on the contents of $fldref->[3] you have to dereference it yet again and do @{$fldref->[3]} or @{@fldref[3]}. This works as expected:

        foreach my $tmp (@{$fldref->[3]}) { print "$tmp: <@$tmp>\n"; } #outputs ARRAY(0x814ec28) <$a Tarina Babarista, pienest{ elefantista> ARRAY(0x81e652c) <$s (26'34); orkestroitu> ARRAY(0x81e64fc) <$d Poulenc, Francis> ARRAY(0x81e5a4c) <$g Brunhoff, Jean de > ARRAY(0x81f236c) <$h P|ysti, Lasse> ARRAY(0x81f239c) <$j Fran*8aix, Jean>

        Hope that helps, beth

        I think you may be confused about Dumper output, and perhaps about what's actually in your arrays. In your first example:
        \@arr = [ 1, 2, 3, [ anna, beth, christie, denise ], ]
        the fourth element in your array is an array ref, not an array. You can only store scalars in an array, so the only way to store an array in another array is to store a reference. The output you show in the second part:
        \[ anna, beth, christie, denise ],
        would actually mean a reference to a reference to an array.
        december:

        I think you are also confused about Perl array and anonymous array syntax. The statement
            my @array = ('a', 'b', 'c');
        constructs a list which is then used to initialize an array. The statement
            my $array_ref = ['a', 'b', 'c'];
        constructs an anonymous array and returns a scalar reference to the array that is then used to initialize a scalar. See discussions of the  [ ] anonymous array constructor in perlref and perlreftut.

        Consider the following examples:

        >perl -wMstrict -MData::Dump=dump -le "my \@arr = [ 1, 2, 3, [ 'anna', 'beth', 'christie', 'denise' ], ]; dump @arr; " syntax error at -e line 1, near "my \" Global symbol "@arr" requires explicit package name at -e line 1. Global symbol "@arr" requires explicit package name at -e line 1. Execution of -e aborted due to compilation errors. >perl -wMstrict -MData::Dump=dump -le "my @arr; \@arr = [ 1, 2, 3, [ 'anna', 'beth', 'christie', 'denise' ], ]; dump @arr; " Can't modify reference constructor in scalar assignment ... near "];" Execution of -e aborted due to compilation errors. >perl -wMstrict -MData::Dump=dump -le "my @arr = [ 1, 2, 3, [ 'anna', 'beth', 'christie', 'denise' ], ]; dump @arr; print scalar @arr; " [1, 2, 3, ["anna", "beth", "christie", "denise"]] 1 >perl -wMstrict -MData::Dump=dump -le "my @arr = ( 1, 2, 3, [ 'anna', 'beth', 'christie', 'denise' ], ); dump @arr; print scalar @arr; " (1, 2, 3, ["anna", "beth", "christie", "denise"]) 4
        Neither of the first two examples compile at all.

        The third example initializes an array with a single element (as shown by the
            print scalar @arr;
        statement), a scalar that is a reference to an anonymously constructed array.

        The fourth example initializes an array with four elements (one of which happens to be a reference to yet another anonymous array).

Re: foreach doesn't select array element
by Anonymous Monk on May 04, 2009 at 08:52 UTC
    Why would the foreach change anything, its the same reference?
    # ALL SAME dump(@$fldref[TXT]); foreach my $tmp (@$fldref[TXT]) { dump($tmp); } my ( $foobar ) = @$fldref[TXT]; dump($foobar);
    You probably want
    foreach my $tmp (@{ $fldref[TXT][0] }) { dump($tmp); }
    When in doubt, show Data::Dumper output

      A lot better, with that @{}...

      ... but I don't understand why Data::Dumper seems to suggest that @$fldref[3] points to an array, see original post – the fourth field is an array, as there's no slash in front of it. That's probably what confuses me.

      Shouldn't foreach (@$fldref[3]) {} automatically use the referenced array or spit out a warning about passing a single ref?

      Thanks for the help, though. I think I've been staring at the same code for too long...

        Shouldn't foreach (@$fldref3) {} automatically use the referenced array or spit out a warning about passing a single ref?

        I didn't read your code in detail, but when de-referencing a subscript'ed thing, you need to use {} to show "@" what it is operating upon, eg.. @{$fldref[3]}. If $fldref is just a single ref, then @$fldref works. But this doesn't work if $fldref has a subscript.

        Shouldn't  foreach (@$fldref[3]) {} automatically use the referenced array or spit out a warning about passing a single ref?
        foreach (see perlsyn) will iterate over the list that you build for it. If you build a list by de-referencing a reference to an array (an array which may have zero or one or more elements) or by simply specifying a single scalar (which may be a reference to an array of zero or more elements), that's your business.