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

Afternoon monks,

2 questions:
1: why does this not work:

print "not ref:\n"; test(dogs=>'bark', cats=>'miaow'); print "ref:\n"; test({dogs=>'bark', cats=>'miaow'}); sub test{ my $params; ref($_[0]) ? $params = shift : $params = {@_}; print Dumper($params); }
The output is
not ref: $VAR1 = { 'cats' => 'miaow', 'dogs' => 'bark' }; ref: Odd number of elements in anonymous hash at params.t line 16. $VAR1 = { 'HASH(0x81194e0)' => undef };
Which would seem to imply that the test is always false, but if I change $params = {@_} to print "hello" I get:
not ref: hello $VAR1 = undef; ref: $VAR1 = { 'dogs' => 'bark', 'cats' => 'miaow' };
So in this case, when it is a reference, the test correctly evaluates to true and doesn't print 'hello'

Also, if I do it like:

if(ref($_[0])){ $params = shift; } else{ $params = {@_}; }
I get exactly what I was expecting:
not ref: $VAR1 = { 'cats' => 'miaow', 'dogs' => 'bark' }; ref: $VAR1 = { 'cats' => 'miaow', 'dogs' => 'bark' };
?????

2. Is there a better way to allow a sub to be called like either test(dogs=>bark, cats=>miaow) or test({dogs=>bark, cats=>miaow})?

Cheers, Cxx

Edit by BazB retitle from ":? wierd"

Replies are listed 'Best First'.
Re: Parameter list wierdness
by dave_the_m (Monsignor) on Jul 01, 2004 at 15:31 UTC
    try replacing
    ref($_[0]) ? $params = shift : $params = {@_};
    with
    $params = ref($_[0]) ? shift : {@_};

    Dave.

    update:

    Note that your line was getting parsed as

    (ref($_[0]) ? $params = shift : $params) = {@_};
      Thanks for the explanation. That's what I get for only reading the first line of the description in perlop. Sorry!

      Cass

Re: Parameter list wierdness
by rir (Vicar) on Jul 01, 2004 at 15:33 UTC
    Try writing your ternary operation like this:
    sub test{ my $params; $params = ref($_[0]) ? shift : {@_}; print Dumper($params); }
    Be well.
      aah, that works and it's much nicer. Still think I'm missing something about how the ternary operator works though!

      Thanks, Cxx

        my $value = (if condition? then this: else this);
        That's about the quickest explanation. Ternary should never be used in void context. See my node Ternary in void context where I cleared up my ternary confusion as well. The ternary operator always returns the value present in its truth branch (for lack of a better term), that's why you shouldn't substitute it for if then constructs.

        an if then construct that warrants a ternary is similar to the following

        if($some_var eq 'X') { $this_var = 'Run using X format'; } else { $this_var = 'Run using standard format'; }
        This would equate to the ternary
        $this_var = ($some_var eq 'X'? 'Run using X format':'Run using standar +d format');

Re: Parameter list wierdness
by davido (Cardinal) on Jul 01, 2004 at 16:58 UTC

    This one took some fiddling for it to occur to me what was the problem.

    Trinary operators can be used as an R-value, or as an L-value. You've constructed a L-value.

    Here's what's happening:
    ref($_[0]) ? $params = shift : $params = {@_};

    When the trinary's conditional is false (no reference), {@_} is assigned to $params. When the trinary's conditional is true, {@_} is assigned to $params = shift. Though the following behavior is undefined, here's why "it" doesn't work. $params = shift shifts the reference out of @_. But then next a hash reference is created using the now empty @_ with the " = {@_} " part. So the first value of $params is discarded, and replaced by a hash-ref to a hash containing only the empty array @_.

    There's some speculation in all that, but it seems to bear out in lightweight testing.


    Dave