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

Whilst we're on the subject of wierd behaviour, can any explain what's going on here?

perl -e"print join( '|', %{{ }} = (1)x100 );"

Specifically, where do the undefined values come from?

Update: With the help of those below and some twiddling of my own, I arrived at this.

P:\test>perl -le"print join'.', %h = (1)x$_ for 1..20;" 1 1.1 1..1 1..1.1 1..1..1 1.1.1..1.1 1..1..1..1 1..1.1.1..1.1 1..1..1..1..1 1.1.1..1.1.1..1.1 1..1..1..1..1..1 1..1.1.1..1.1.1..1.1 1..1..1..1..1..1..1 1.1.1..1.1.1..1.1.1..1.1 1..1..1..1..1..1..1..1 1..1.1.1..1.1.1..1.1.1..1.1 1..1..1..1..1..1..1..1..1 1.1.1..1.1.1..1.1.1..1.1.1..1.1 1..1..1..1..1..1..1..1..1..1 1..1.1.1..1.1.1..1.1.1..1.1.1..1.1

Which doesn't explain anything, but it removes the ofuscation a little, and clarifies the bizareness.

By now I'm assuming this is a bug, and I'll report it if noone disagree's by later today.

I really can't wait to see the set of circumstances that lead to such a variable set of outputs. The offending piece of code should be immortalizes on a t-shirt, along with the name of which ever clever person tracks it down.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!

update (broquaint): title change (was Erm?)

Replies are listed 'Best First'.
Double Erm?
by Zaxo (Archbishop) on Oct 29, 2003 at 04:58 UTC

    Trying to puzzle out what the heck is meant by %{{ }} = LIST, I did

    $ perl -MO=Deparse -e'print join( "|", %{{ }} = (1)x100 );' print join('|', %{{};} = (1) x 100); -e syntax OK $ perl -e'print join( "|", %{{};} = (1)x100 );' syntax error at -e line 1, near "%{{};" Unmatched right curly bracket at -e line 1, at end of line Execution of -e aborted due to compilation errors. $
    That looks like a borderline syntax error, since perl parses the code to something just as weird, but differently. Or maybe it's an O::Deparse problem.

    The behavior you noted goes away if all the hash key slots are different:

    $ perl -we'print join( "|", %{{ }} = 1..100 );' 1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|2 +7|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|5 +0|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|7 +3|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|9 +6|97|98|99|100$ $ perl -we'print join( "|", %{{ }} = (1,1,2,1,3,1,4,1,5,1) );' 1|1|2|1|3|1|4|1|5|1$
    Warnings are on, so the weirdness would show noisily.

    After Compline,
    Zaxo

      It must be an O::Deparse problem, as what's meant by %{{ }} = LIST isn't really all that odd. The construct: %{ ... } is simply a hash-dereference. Remember that the % just means a hash dereference on its own and that the braces serve as parenthetical operators with respect to references. Inside of the hash-dereference construct is just {} which is nothing more than an anonymous hash reference. So, %{{ }} is nothing more that an empty anonymous hash, in the same sense that @{[]} is an empty anonymous array.

      Oh, and by the way, the issue at hand doesn't have anything to do with the fact that he uses an anonymous hash... you get the same behavior if you drop %x in in place of %{{ }}.


      ------------
      :Wq
      Not an editor command: Wq
Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by Abigail-II (Bishop) on Oct 29, 2003 at 10:49 UTC
    With some experimenting, I get strange results if the following conditions are all true:
    • There is a hash assignment in list context.
    • There's a duplicate key on the RHS of the assignment.
    Minimal test cases:
    $ perl -le '[%h = (1 .. 4)]' # Fine. $ perl -le '[%h = (1) x 4]' Bizarre copy of ARRAY in anonlist at -e line 1.
    Also:
    $ perl -le 'print join ".", %h = 1 .. $_ for 1 .. 20' 1 1.2 1.2.3 1.2.3.4 1.2.3.4.5 1.2.3.4.5.6 1.2.3.4.5.6.7 1.2.3.4.5.6.7.8 1.2.3.4.5.6.7.8.9 1.2.3.4.5.6.7.8.9.10 1.2.3.4.5.6.7.8.9.10.11 1.2.3.4.5.6.7.8.9.10.11.12 1.2.3.4.5.6.7.8.9.10.11.12.13 1.2.3.4.5.6.7.8.9.10.11.12.13.14 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20

    It's interesting to note that the resulting numbers are still in order - what clearly isn't happening is that the list to join is gotten out of the hash (in keys() order). So I guess that some aliasing into the list on the RHS plays a role, some values are probably munged when inserting them into the hash, causing a collision.

    Interesting might also be:

    $ perl -le 'print join ".", %h = map {(1, $_)} 1 .. $_ for 1 .. 10' 1.1 1..1.2 1.3.1..1.3 1..1.4.1..1.4 1.5.1..1.5.1..1.5 1..1.6.1..1.6.1..1.6 1.7.1..1.7.1..1.7.1..1.7 1..1.8.1..1.8.1..1.8.1..1.8 1.9.1..1.9.1..1.9.1..1.9.1..1.9 1..1.10.1..1.10.1..1.10.1..1.10.1..1.10

    Abigail

      Agreed. Although not all list assignments seem to do it, and you don;t need to be using either join or x or print?

      I'm pretty sure that this is an over zealous optimisation -- reusing an existing stack frame or some such.

      By tying the hash, the problem disappears.

      P:\test>perl sub TIEHASH { return bless {}, 'main'; } sub STORE{ $_[0]->{$_[1]} = $_[2]; } sub FETCH{ $_[0]->{$_[1]} } sub CLEAR{ undef %{$_[0]} } tie %h, 'main'; print %h = ( 1,1,1,1); ^Z 1111

      I tried using B::Concise to isolate a difference between a bizarre result and an expected one.

      Normal

      P:\test>perl -we"print map 1, %h = (1,1,1,1);" Name "main::h" used only once: possible typo at -e line 1. 1111 P:\test>perl -MO=Concise -we"print map 1, %h = (1,1,1,1);" Name "main::h" used only once: possible typo at -e line 1. i <@> leave[t1] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v ->3 h <@> print vK ->i 3 <0> pushmark s ->4 f <|> mapwhile(other->g)[t4] lK/1 ->h e <@> mapstart lK/2 ->f 4 <0> pushmark s ->5 - <1> null lK/1 ->5 g <$> const(SPECIAL Null)[t9] s ->f d <2> aassign[t3] lKMS ->e - <1> ex-list lKP ->a 5 <0> pushmark s ->6 6 <$> const(SPECIAL Null)[t5] s ->7 7 <$> const(SPECIAL Null)[t6] s ->8 8 <$> const(SPECIAL Null)[t7] s ->9 9 <$> const(SPECIAL Null)[t8] s ->a - <1> ex-list lK ->d a <0> pushmark s ->b c <1> rv2hv[t2] lKRM*/1 ->d b <#> gv s ->c -e syntax OK

      Bizarre

      P:\test>perl -we"print %h = (1,1,1,1);" Name "main::h" used only once: possible typo at -e line 1. Use of uninitialized value in print at -e line 1. 111 P:\test>perl -MO=Concise -we"print %h = (1,1,1,1);" Name "main::h" used only once: possible typo at -e line 1. e <@> leave[t1] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v ->3 d <@> print vK ->e 3 <0> pushmark s ->4 c <2> aassign[t3] lKS ->d - <1> ex-list lKP ->9 4 <0> pushmark s ->5 5 <$> const(SPECIAL Null)[t4] s ->6 6 <$> const(SPECIAL Null)[t5] s ->7 7 <$> const(SPECIAL Null)[t6] s ->8 8 <$> const(SPECIAL Null)[t7] s ->9 - <1> ex-list lK ->c 9 <0> pushmark s ->a b <1> rv2hv[t2] lKRM*/1 ->c a <#> gv s ->b -e syntax OK

      The only difference I can see is entirely due to the map. Exclude that, and the rest is identical.

      So, a bug? If it is, it's been arounf since at least 5.6.1!


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!

        The only difference I can see is entirely due to the map. Exclude that, and the rest is identical.
        Well, yeah, but the map makes that you get four new 1's.
        print map 1, 1, 1, undef, 1;
        will print out "1111".
        So, a bug? If it is, it's been arounf since at least 5.6.1!
        I think it's a bug. I think it will be hard to fix. And it has been around since at least 5.005.

        Abigail

        Your tied hash example still displays broken-ness:
        perl -e "%h = (1,1,1,1); print %h"
        gives
        11
        not the
        1111
        displayed in your tied hash example.

        rdfield

Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by sauoq (Abbot) on Oct 29, 2003 at 05:36 UTC

    Trying to wittle down your example gave me:

    $ perl -le 'print for %h = (1,1,1,1);' 1 1 1
    It definitely has something to do with the result of the hash assignment with duplicate keys being interpreted as a list last.
    $ perl -le 'print join ".", @a = %h = (1,1,1,1);' 1.1.1.1
    Seems to fix it, but
    $ perl -le 'print join ".", %h = @a = (1,1,1,1);' 1..1.1
    breaks it again.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by Roger (Parson) on Oct 29, 2003 at 04:53 UTC
    Here is my attempt to explain part of what is happening - in the the code below:
    print join( '|', %{{ }} = (1)x100 );
    { } creates a reference to an empty anonymous hash

    %{ { } } is the name of the dereferenced anonymous hash.

    %{{ }} = (1) x 100 assigns 50 hash entries to the anonymous hash, before it's values are printed.

    In fact, I can do the following:
    use strict; print join '|', %{{ 'X' => 1, 'Y' => 1 }} = (1)x100;
    as long as the what's inside the anonymous hash forms a legal hash, which is an empty hash in your case.

    I am still scratching my head trying to figure out the %hash = (1)x100 part...

    It seems
    my %x; my @x = (1) x 100; print join '|', %x = @x;
    produces the same result...

Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by Mr. Muskrat (Canon) on Oct 29, 2003 at 04:48 UTC

    Intriguing...

    C:\Documents and Settings\Administrator\Desktop>perl -MO=Deparse -e"pr +int join('|', %{{ }} = (1)x100 );" print join('|', %{{};} = (1) x 100); -e syntax OK

    C:\Documents and Settings\Administrator\Desktop>perl -MData::Dumper -e + "print Dumper( %{{ }} = (1) x 100);" Bizarre copy of ARRAY in anonlist at C:/Perl/lib/Data/Dumper.pm line 4 +45.

    The conversion from list to hash is confusing things?

      Better yet... it appears to be entirely tied to the context of the hash-assignment operation, in a totally bizzare way:
      [me@host]$ perl -e '$x = [%y = (1,2,1,2)];' Bizarre copy of ARRAY in anonlist at -e line 1. [me@host]$ perl -e '%y = (1,2,1,2); $x = [%y];' [me@host]$

      ------------
      :Wq
      Not an editor command: Wq
Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by rdfield (Priest) on Oct 29, 2003 at 09:50 UTC
    Perhaps you're asking the wrong question:
    perl -MData::Dumper -e "for (1 .. 6){print join('.',%h = (1) x $_) . \ +"\n\";print Dumper(\%h)}"
    results in the already observed behaviour
    1 $VAR1 = { '1' => undef }; 1.1 $VAR1 = { '1' => 1 }; 1..1 $VAR1 = { '1' => undef }; 1..1.1 $VAR1 = { '1' => 1 }; 1..1..1 $VAR1 = { '1' => undef }; 1.1.1..1.1 $VAR1 = { '1' => 1 };
    whereas
    perl -MData::Dumper -e "for (1 .. 6){%h=(1) x $_;print join('.',%h ) . + \"\n\";print Dumper(\%h)}"
    results in
    1. $VAR1 = { '1' => undef }; 1.1 $VAR1 = { '1' => 1 }; 1. $VAR1 = { '1' => undef }; 1.1 $VAR1 = { '1' => 1 }; 1. $VAR1 = { '1' => undef }; 1.1 $VAR1 = { '1' => 1 }
    which is correct. The question would seem to me to be "why is the 'x' operator propogating to the join?". My guess is that the join is executed for every iteration of the "x", i.e. every time the assignment is triggered %h is updated and the join is executed. (BTW, the undef values are correct and to be expected - see the Dumper results above).

    rdfield

      (BTW, the undef values are correct and to be expected...

      I'm not sure how you reach that conclusion? When you assign an even sized list to a hash, every other value starting with the first is a key, and the next is the value assigned to that key.

      perl -e"%h = (1=>2, 2=>4, 5=>6, 7=>8, 9=>10); $,='-'; print %h;" 1-2-7-8-9-10-2-4-5-6
      If the keys are all the same, then then hash should end up with a single key, with the value of the last item in the list.
      perl -e"%h = (1=>2, 1=>4, 1=>6, 1=>8, 1=>10); $,='-'; print %h;" 1-10

      As you can see, I've eliminated both the join and the X from the equation, but still...

      P:\test>perl -e"$,='-'; print %h = (1,1,1,1,1,1,1,1,1,1);" 1-1-1--1-1-1--1-1

      If I do

      perl -e"$s = 'fred'; print $s;" fred perl -e"print $s = 'fred';" fred perl -e"$,='-'; @a=(1,1,1,1,1,1,1,1,1,1); print @a;" 1-1-1-1-1-1-1-1-1-1 perl -e"$,='-'; print @a=(1,1,1,1,1,1,1,1,1,1);" 1-1-1-1-1-1-1-1-1-1

      It doesn't matter whether I assign then print or print the assignment, the results are the same. With a hash the results are different.

      The real kicker is that after the print assignment, the hash ends up correct.

      perl -MData::Dumper -le"$,='-'; print %h = (1,1,1,1,1,1,1,1,1,1); prin +t Dumper \%h;" 1-1-1--1-1-1--1-1 $VAR1 = { '1' => 1 };

      It's only the results of printing the assignment that is in error...at least that I've so far discovered.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!

        It's only the results of printing the assignment that is in error...at least that I've so far discovered.

        Well, it doesn't have anything to do with the print() really. This avoids printing it directly:

        $ perl -le '$_ = join "-", %h = (1,2,3,4,1,2,3,4); print "yes" if /--/ +' yes

        By the way, I've noticed that map seems to make the problem go away (as does assigning to an array) but grep, reverse, and sort don't:

        map { $_ } %h = ( 1,1, 1,1 ); # no undefined values. grep { 1 } %h = ( 1,1, 1,1 ); # undefs occur. sort %h = ( 1,1, 1,1 ); # undefs occur. reverse %h = ( 1,1, 1,1 ); # undefs occur.

        -sauoq
        "My two cents aren't worth a dime.";
        
        Sorry, BrowserUk, looks like I missed detailing a step in my reasoning: the list to array assignment seems to happen in one "go", so the contents on the LHS of the assignment would only be printed once, whereas coercing a list to a hash appears result in the LHS being updated for every element of the list, and the assignment propogating before it is complete resulting in too many values being printed/joined.

        From my observataions it would seem that the following is being printed: for even numbers of pairs (4 elements, 8 element, 12 elements):

        "1--1-1" x (elements/4)
        (ie the print is call twice, once for key 1 having value undef, once for key value 1 having value 1) and for odd numbers of pairs (2 elements, 6 elements, 10 elements etc):
        "1-1" . "1--1-1" * floor(elements/4)
        (ie print once where key 1 has value 1, then proceed as previously).

        rdfield

Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by shenme (Priest) on Oct 29, 2003 at 04:33 UTC
    Just to add some detail,
    DB<1> print join( '|', %{{ }} = (1,2,1,4,1,6,1,8) ); DB<2> 1||1|8|1||1|8
    but otherwise I'm mystified too
Re: Erm? Bug or not? Weird behaviour in hash / list conversion
by Qtax (Initiate) on Oct 29, 2003 at 17:18 UTC
    This side of the bug has a nice effect too:
    perl -le "$,='-'; @a=%h=(a=>1, a=>2); print @a" a-a-a-2
    Did anyone notice this? So having an @a in print @a = %h=(1,1,1,1); wont fix it really.