http://qs1969.pair.com?node_id=1176731

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

Dear Monks

Certain purveyors of half truths have led me to believe that the "? :" syntax and the "if then else" syntax spring from the same goodly source. yet i say one is a calumny of the very perl itself. to wit :-

my $a = 'a'; my $b = 'b'; 1 ? $a = 2 : $b = 'x'; print "a is $a\n"; print "b is $b\n";

outputs

a is x b is b

Replies are listed 'Best First'.
Re: ternary conditional help
by tybalt89 (Monsignor) on Nov 28, 2016 at 20:34 UTC

    Because of precedence

    (1 ? $a = 2 : $b) = 'x';

    You didn't read your "perldoc perlop" ...

      ok thanks, but how is it that  ($a = 2) = 'x' makes $a = 'x' ?

        Hi opaltoot,

        See Assignment Operators:

        ... the scalar assignment operator produces a valid lvalue. Modifying an assignment is equivalent to doing the assignment and then modifying the variable that was assigned to. This is useful for modifying a copy of something, like this:

        ($tmp = $global) =~ tr/13579/24680/;

        ... Likewise,

        ($x += 2) *= 3;

        is equivalent to

        $x += 2; $x *= 3;

        Hope this helps,
        -- Hauke D

Re: ternary conditional help
by haukex (Archbishop) on Nov 28, 2016 at 20:42 UTC

    Hi opaltoot,

    tybalt89 already gave you the answer, I just wanted to note you can use B::Deparse to see how Perl interprets your code for yourself (I replaced 1 with $x to prevent optimization):

    $ perl -MO=Deparse,-p -e '$x ? $a = 2 : $b = "x";' (($x ? ($a = 2) : $b) = 'x'); $ perl -MO=Deparse,-p -e '$x ? ($a = 2) : ($b = "x");' ($x ? ($a = 2) : ($b = 'x'));

    See also Operator Precedence and Associativity.

    Although you might want to consider if using the ternary operator like that will help the legibility of the code :-)

    Hope this helps,
    -- Hauke D

Re: ternary conditional help (style)
by tye (Sage) on Nov 28, 2016 at 21:57 UTC
    Certain purveyors of half truths have led me to believe that the "? :" syntax and the "if then else" syntax spring from the same goodly source.

    Equivalent uses of them likely produce identical opnodes for Perl to execute. But "if" is meant for use on statements (or blocks of statements). "? :" is meant for use on expressions. The precedence table (for Perl and C and many other similar languages) treat assignment as more like a statement, not just an expression. So, to include an assignment inside of an expression, you usually need to enclose that assignment inside parens.

    This can be a good reason to adopt a style that avoids using "? :" to pick between different assignments. On the flip side, I have had people argue that code like:

    if( condition() ) { $a = one(); } else { $a = two(); }

    is better written, style-wise, like:

    $a = condition() ? one() : two();

    Which I can agree with in some cases more than others.

    - tye        

Re: ternary conditional help
by AnomalousMonk (Archbishop) on Nov 29, 2016 at 00:25 UTC

    In addition to the deparse examples provided by haukex here, consider the OPed code and a slight modification. In the first, original version, constant folding (I think that's the correct phrase) makes the ternary go away and reduces the expression to a pure assignment (albeit a two-step one):

    c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "my $a = 'a'; my $b = 'b'; 1 ? $a = 2 : $b = 'x'; print qq{a is $a}; print qq{b is $b}; " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my $a = 'a'); (my $b = 'b'); (($a = 2) = 'x'); print("a is $a"); print("b is $b"); -e syntax OK
    Making the condition expression of the ternary non-constant preserves the ternary operator in all its LHS glory:
    c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "my $a = 'a'; my $b = 'b'; my $p = 1; $p ? $a = 2 : $b = 'x'; print qq{a is $a}; print qq{b is $b}; " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my $a = 'a'); (my $b = 'b'); (my $p = 1); (($p ? ($a = 2) : $b) = 'x'); print("a is $a"); print("b is $b"); -e syntax OK
    Please see O and B::Deparse.


    Give a man a fish:  <%-{-{-{-<

Re: ternary conditional help
by BillKSmith (Monsignor) on Nov 29, 2016 at 03:59 UTC
    The operator is intended to return a single value. Attempting to get side effects is a bad idea even if you succeed. Here is how to get the result you desire.
    use strict; use warnings; my $logical = 1; my ($a,$b); $a = $logical ? 2 : 'a'; $b = $logical ? 'b' : 'x'; print "a is $a\n"; print "b is $b\n"; $logical = 0; print "\$logical is now false\n"; $a = $logical ? 2 : 'a'; $b = $logical ? 'b' : 'x'; print "a is $a\n"; print "b is $b\n"; OUTPUT: a is 2 b is b $logical is now false a is a b is x
    Bill