Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

unexpected modify hash in a distance with grep { $_ }

by leszekdubiel (Scribe)
on Dec 20, 2019 at 17:29 UTC ( [id://11110437]=perlquestion: print w/replies, xml ) Need Help??

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

Hello Great Monks! Could anyone explain why this program dies? "x" valu in hashref $h shouldn't be modified... in the same manner as "y" was not modified...
#!/usr/bin/perl -CSDA use utf8; use Modern::Perl qw{2017}; use Data::Dumper; $Data::Dumper::Sortkeys = 1; my $h = {a => 1, b => "", c => 2}; my $d1 = Dumper($h); printf "grep: %s\n", (join " ... ", grep { $_ } $$h{a}, $$h{x}, $$h{b +}, $$h{c}); printf "test: %s\n", $$h{y} ? "yes" : "no"; my $d2 = Dumper($h); $d1 eq $d2 or die "\nFAIL! h{x} modified, h{y} not modified... why?\n +\n$d1\n$d2";
result:
grep: 1 ... 2 test: no FAIL! h{x} modified, h{y} not modified... why? $VAR1 = { 'a' => 1, 'b' => '', 'c' => 2 }; $VAR1 = { 'a' => 1, 'b' => '', 'c' => 2, 'x' => undef };

Replies are listed 'Best First'.
Re: unexpected modify hash in a distance with grep { $_ }
by LanX (Saint) on Dec 20, 2019 at 18:26 UTC
    Hi

    the $_ inside the grep is an alias of the hash-values, i.e. any assignment would be mirrored.

    For reasons that I don't understand yet (under-coffeination perhaps) this triggers an autovivification. °

    compare Re^2: unexpected modify hash in a distance with grep { $_ }

    This can easily circumvented with a more common approach to only pass the keys.

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $h = { 'a' => 1, 'b' => 2, 'c' => 3, }; warn Dumper [ $$h{a}, $$h{x}, $$h{b}, $$h{c} ]; warn Dumper [ keys %$h ]; warn Dumper [ grep {$$h{$_}} qw/a b c x/ ]; # <-- no side effect warn Dumper [ keys %$h ];

    C:/Perl_524/bin\perl.exe d:/tmp/pm/grep_autovivify.pl $VAR1 = [ 1, undef, 2, 3 ]; $VAR1 = [ 'a', 'c', 'b' ]; $VAR1 = [ 'a', 'b', 'c' ]; $VAR1 = [ 'a', 'c', 'b' ];

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

    °) my guess is it's an implementation detail/bug

      It seems to be a general issue that accessing an alias is sometimes triggering autovivification.

      Didn't know that. Smells like bug.

      #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $h = { 'a' => 1, 'b' => 2, 'c' => 3, }; warn "x: ",$_ for $$h{x}; # autovivifiaction via alias warn Dumper [ keys %$h ]; tst($$h{y}); # no autovivification warn Dumper [ keys %$h ]; sub tst { warn "y: ",$_[0] ; }

      C:/Perl_524/bin\perl.exe d:/tmp/pm/grep_autovivify.pl Use of uninitialized value $_ in warn at d:/tmp/pm/grep_autovivify.pl +line 13. x: at d:/tmp/pm/grep_autovivify.pl line 13. $VAR1 = [ 'c', 'x', 'b', 'a' ]; Use of uninitialized value $_[0] in warn at d:/tmp/pm/grep_autovivify. +pl line 24. y: at d:/tmp/pm/grep_autovivify.pl line 24. $VAR1 = [ 'c', 'x', 'b', 'a' ];

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        grep (and for) provide lvalue context for their args, since the expectation is that when aliased to $_, the $_ variable (and thus the arg) could be modified by the code in the code block. This lvalue context is what causes the autovivification action to be compiled in. The 'x' element of %$h is created at run-time when the arg list for grep is being assembled, but before grep itself is invoked (i.e. before the code block is called for the first time).

        However, function call arguments which are hash subscripts are special-cased. Although function args are technically always in lvalue context (e.g. the sub about to be called might do sub f { $_[0]++ }, who knows!), perl tries to avoid auto-vivification by deferring hash lookup. Instead, if you do foo($h{$k}) then rather than looking up the hash value and passing it as an arg, perl creates a special proxy object (PVLV) which holds pointers to $h and the key and passes that instead. For example:

        $ perl -MDevel::Peek -e'sub f { Dump $_[0] } f($h{akey})' SV = PVLV(0xd28a78) at 0xcb2658 REFCNT = 1 FLAGS = (GMG,SMG) IV = 0 NV = 0 PV = 0 MAGIC = 0xce9828 MG_VIRTUAL = &PL_vtbl_defelem MG_TYPE = PERL_MAGIC_defelem(y) MG_FLAGS = 0x02 REFCOUNTED MG_OBJ = 0xcb2820 SV = PV(0xcb2ed8) at 0xcb2820 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0xce97f8 "akey"\0 CUR = 4 LEN = 10 TYPE = y TARGOFF = 0 TARGLEN = 1 TARG = 0xce1a80 FLAGS = 0 SV = PVHV(0xcb8218) at 0xce1a80 REFCNT = 2 FLAGS = (SHAREKEYS) ARRAY = 0x0 KEYS = 0 FILL = 0 MAX = 7
        If this PVLV values is assigned to then it autovivifies the hash. If it is just read, it doesn't. It behaves kinda like a tied variable. Personally I don't like like this behaviour much, but it was added in 5.004, before my time as a p5 porter.

        Dave.

      the $_ inside the grep is an alias of the hash-values, i.e. any assignment would be mirrored. For reasons that I don't understand yet (under-coffeination perhaps) this triggers an autovivification.

      My educated guess is that it's because autovivification generally happens in an lvalue context.

Re: unexpected modify hash in a distance with grep { $_ }
by afoken (Chancellor) on Dec 20, 2019 at 17:42 UTC
    printf "grep: %s\n", (join " ... ", grep { $_ }  $$h{a}, $$h{x}, $$h{b}, $$h{c});

    Not a problem with grep. $$h{x} is autovivified before grep happens.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      > Not a problem with grep. $$h{x} is autovivified before grep happens.

      Nope, it happens because of the aliasing of the hash value inside grep, but I'm not sure how.

      #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $h = { 'a' => 1, 'b' => 2, 'c' => 3, }; warn Dumper [ $$h{a}, $$h{x}, $$h{b}, $$h{c} ]; warn Dumper [ keys %$h ]; warn Dumper [ grep {$_} $$h{a}, $$h{x}, $$h{b}, $$h{c} ]; warn Dumper [ keys %$h ]; warn !$$h{y}; warn Dumper [ keys %$h ];

      C:/Perl_524/bin\perl.exe d:/tmp/pm/grep_autovivify.pl $VAR1 = [ 1, undef, 2, 3 ]; $VAR1 = [ 'b', 'a', 'c' ]; $VAR1 = [ 1, 2, 3 ]; $VAR1 = [ 'a', 'x', 'c', 'b' ]; 1 at d:/tmp/pm/grep_autovivify.pl line 19. $VAR1 = [ 'a', 'x', 'c', 'b' ];

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        Nope, it happens because of the aliasing of the hash value inside grep, but I'm not sure how.

        Interesting effect. I see your output with plain perl v5.22.2 (from 2016) on Linux x86-64 and with Strawberry v5.14.2 (from 2011) on Windows 64 bit. So this seems to be an old, unsolved problem.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11110437]
Approved by Paladin
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-20 00:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found