in reply to Keys() required to autovivify?

Your code is overly complicated. $h{$x} can be replaced with just $h.

>perl -le"%{ $h }; print $h ? 1 : 0" 0 >perl -le"%{ $h } = (); print $h ? 1 : 0" 1 >perl -le"keys %{ $h }; print $h ? 1 : 0" 1 >perl -le"sub {}->( %{ $h } ); print $h ? 1 : 0" 1

In the first program, the hash isn't required, so it isn't created.
In the subsequent programs, a hash is required, so it is autovivified.

I can't explain why some built-ins don't autovivify the hash, even though it needs to be flattened.

>perl -le"sub f {} f( %{ $h } ); print $h ? 1 : 0" 1 >perl -le"print( %{ $h } ); print $h ? 1 : 0" 0

Replies are listed 'Best First'.
Re^2: Keys() required to autovivify?
by jrw (Monk) on Dec 30, 2007 at 07:52 UTC
    Well, actually my code could just be replaced with perl -e ''. But this is just my demo code to show the problem that I encountered.

    My real code looked like:

    if (%{$h{x}}) { ... }
    by which I meant to execute the body of the if statement only if $h{x} (was a hash and) had at least one element. But instead I got a syntax error. I had to switch to
    if (keys %{$h{x}}) { ... }
    which was more than I wanted to say.

    I was wondering why the use of keys() made perl smart enough to autovivify my hash, but the use of %{$h{x}} wasn't enough to cause it to autovivify (and then return a false value since the autovivified hash value was empty).

      only if $h{x} (was a hash and) had at least one element

      Then test whether the value of $h{x} actually is a hash reference.

      If you say keys %{$h{x}} it is assumed you know what you are doing and want autovivifying, while dereferencing an undef value could just be a mistake. strict 'refs' catches that - that is what strict is about ;-)

      By using keys you look inside the box (= the hashref), so perl autovivifies it, if it doesn't exist; by dereferencing you look at the box only.

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
        Hmm. I'm not convinced. By using %{$h{x}}, I'm not saying anything different: i.e. I expect $h{x} to hold a hash, and I want it autovivified if $h{x} is undef. By using keys(%{$h{x}}), I'm saying: give me the list (or count) of keys in the hash in $h{x} and autovivify it if it's not there (returning an empty list or 0 from keys).
Re^2: Keys() required to autovivify?
by shmem (Chancellor) on Jan 02, 2008 at 14:52 UTC
    I can't explain why some built-ins don't autovivify the hash, even though it needs to be flattened.

    It's not about hash flattening, but the fact that arguments are passed into subroutines as references. So saying f( %{ $h } ); for an undefined $h looks up keys and values, which causes the container to spring into existence as a hash reference and be assigned to $h. That is necessary since the arguments to subs are potential candidates for assignment (lvalues)

    perl -MO=Concise -le 'my $h;sub f {} f( %{ $h } );' a <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v ->3 3 <0> padsv[$h:1,4] vM/LVINTRO ->4 4 <;> nextstate(main 4 -e:1) v ->5 9 <1> entersub[t4] vKS/TARG,1 ->a - <1> ex-list K ->9 5 <0> pushmark s ->6 7 <1> rv2hv[t3] lKM/1 ->8 - <@> scope sK ->7 - <0> ex-nextstate v ->6 6 <0> padsv[$h:1,4] sM/DREFHV ->7 - <1> ex-rv2cv sK/1 ->- 8 <#> gv[*f] s ->9

    (note the line padsv[$h:1,4] sM/DREFHV with the M flag), which is not the case with print.

    perl -MO=Concise -le 'my $h;print( %{ $h } )' 9 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v ->3 3 <0> padsv[$h:1,3] vM/LVINTRO ->4 4 <;> nextstate(main 3 -e:1) v ->5 8 <@> print vK ->9 5 <0> pushmark s ->6 7 <1> rv2hv[t2] lK/1 ->8 - <@> scope sK ->7 - <0> ex-nextstate v ->6 6 <0> padsv[$h:1,3] s ->7

    Since print doesn't comprise modifcation of it's arguments, the hashref isn't created.

    That's the most concise explanation of this oddity I can conceive up to now...

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Function arguments are always passed by reference, so they need to be real. But print's not a function. Keep in mind that the builtin functions are often called "named operators" and that Deparse shows a "print" op, not a "entersub" op. I suspect it's more along the lines of print and other builtins have their arguments built specially.

      Just guessing.

      By the way, I was getting similar results to print when using assignments.

        Yeah, but I guess it is the entersub op which forces autovivifying of that undef value into a hash ref, to provide write access. How to verify? "Use the source, luke." ;-)

        That's the sad thing about perl5 - the implementation is the spec. But then it is also its glory, as Abigail puts it:

        Programming in Perl5 is like exploring a large medieval castle, surrounded by a dark, mysterious forest, with something new and unexpected around each corner. There are dragons to be conquered, maidens to be rescued, and holy grails to be quested for. Lots of fun.

        I'll have a bit of fun, when I have time to spend...

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}