Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^2: Arrow Operator Question

by BillKSmith (Monsignor)
on Mar 27, 2023 at 14:24 UTC ( [id://11151257] : note . print w/replies, xml ) Need Help??


in reply to Re: Arrow Operator Question
in thread Arrow Operator Question

I prefer to implement kcott's idea this way. "Short circuit" operation of the and operator C style Logical And prevents unnecessary testing which would cause autovivification.
use strict; use warnings; use Data::Dumper; use Test::More tests=>2; my $h = { b => {} }; my $f = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d}); is_deeply( $h, { b => {} }, 'no autovivification' ); is( $f, undef, 'no value assigned' );

OUTPUT:

1..2 ok 1 - no autovivification ok 2 - no value assigned
Bill

Replies are listed 'Best First'.
Re^3: Arrow Operator Question
by hv (Prior) on Mar 27, 2023 at 17:28 UTC
    my $f = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d});

    This is dangerous: the nature of my $var = ... if ... is predictable but very noninuitive (it acts something like a state variable). It has long been regarded as a bug that perl core would dearly love to fix, but cannot due to back-compat.

    I recommend always either splitting out the declaration from the assignment:

    my $f; $f = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d});

    .. or using state explicitly when that's what you actually intend:

    use feature 'state'; # 'use 5.10' or later also gives this automatica +lly state $f = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d +});
      I was a bit surprised that my my did work as I intended. I choose to use it in the interest of conforming to the original code as much as possible. Thanks for the explanation.
      Bill
Re^3: Arrow Operator Question
by kcott (Archbishop) on Mar 27, 2023 at 16:57 UTC

    G'day Bill,

    That's certainly a valid way to go. Consider the following:

    $ perl -e ' use strict; use warnings; use Test::More tests => 8; my $h = { b => {} }; my ($f, $f2, $f3, $f4); $f = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d}) +; is_deeply( $h, { b => {} }, "no autovivification" ); is( $f, undef, "no value assigned" ); $f2 = $h->{b}{c}{d} if exists $h->{b}{c}; is_deeply $h, { b => {} }, "f2: no autovivification"; is $f2, undef, "f2: no value assigned"; $f3 = $h->{b}{c}{d}{x}{y}{z} if exists $h->{b}{c} and exists $h->{b}{c}{d} and exists $h->{b}{c}{d}{x} and exists $h->{b}{c}{d}{x}{y}; is_deeply $h, { b => {} }, "f3: no autovivification"; is $f3, undef, "f3: no value assigned"; $f4 = $h->{b}{c}{d}{x}{y}{z}; delete $h->{b}{c}; is_deeply $h, { b => {} }, "f4: autovivification removed"; is $f4, undef, "f4: no value assigned"; '

    Update: The code above is a modification of the original. Something was niggling me about what I first wrote, but I couldn't see the problem. ++hv's response to your post alerted me to the issue. My first, less-than-good effort is in the spoiler below. Note that the output is unchanged.

    Output:

    1..8 ok 1 - no autovivification ok 2 - no value assigned ok 3 - f2: no autovivification ok 4 - f2: no value assigned ok 5 - f3: no autovivification ok 6 - f3: no value assigned ok 7 - f4: autovivification removed ok 8 - f4: no value assigned
    • Short-circuiting is not actually needed in your example (see f2)
    • Short-circuiting works but can be unwieldy with more complex data structures (see f3)
    • Allowing autovivification then removing it results in cleaner, and easier to maintain, code (see f4)
      • This option has potential drawbacks. Consider the case where $h->{b}{c}{q} existed and had a meaningful and required value but is removed by 'delete $h->{b}{c}'.
      • On second thoughts: this is probably a very bad idea (even though it does work in this specific test script).

    I think each has its merits and probably comes down to best choice on a case by case basis.

    — Ken

      sectokia only showed us the fail case. I assume that his code worked as he expected in the pass case. I should have demonstrated that my code produced the same result in that case.
      use strict; use warnings; use Data::Dumper; use Test::More tests=>2; my $h = { b => { c => { d => 'value' } } }; my $g = $h->{b}{c}{d} if exists($h->{b}{c}) and exists($h->{b}{c}{d}); my $f = $h->{b}{c}{d}//undef; # Original Code is( $g, $f, $f ); is_deeply( $h, { b => { c => { d => 'value' } } }, 'no change' );

      The difference between your various f's is not merely one of style, but one of requirements (which we really do not know).

      Bill