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

Hi,

REWRITTEN, to simplify - and retitled accordingly. (Hope that doesn't cause any angst.)
The following script contains two ternary expressions - both of which start by evaluating the condition ($x).
The first ternary expression apparently evaluates the condition ($x) as TRUE.
But the second apparently evaluates the same condition (with the very same input $x) as FALSE.
use strict; use warnings; my $x = '1'; my $z = "undetermined\n"; ($x) ? print "true\n" : print "false\n"; ($x) ? $z = "true\n" : $z = "false\n"; print $z; __END__ Output (on both perl-5.38.0 and perl-5.40.0): true false
How is this apparent difference explained ?

Cheers,
Rob

Replies are listed 'Best First'.
Re: Evaluating the condition ($x)
by hv (Prior) on Aug 26, 2024 at 03:36 UTC

    It is best explained using "deparse with extra parentheses":

    % perl -MO=Deparse,-p -ce '($x) ? $z = "true" : $z = "false";' (($x ? ($z = "true") : $z) = "false"); -e syntax OK %

    That's a lot of parens, the important pair is: ($x ? $z = "true" : $z) = "false".

    The precedence is such that in this case, the ternary expression is $x ? $z  = "true" : $z. Let's call the result of that $y. That then forms the left hand side of $y = "false". So when $x is true it assigns "true" to $z, then yields $z as an lvalue to apply the second assignment $z = "false", immediately overwriting the "true".

    The moral of the story is: put disambiguating parens round all but the simplest expressions in a ternary operator.

    % perl -wle '$x = 1; $x ? ($z = "true") : ($z = "false"); print $z' true % # or of course make them simple expressions: % perl -wle '$x = 1; $z = $x ? "true" : "false"; print $z' true %
Re: Evaluating the condition ($x) (ternary conditional operator)
by LanX (Saint) on Aug 26, 2024 at 11:00 UTC
    You are "sabotaging" one of the main use cases for the ternary conditional operator : to return an (l)value. ¹ ²

    Better use if-then-else construct instead, if you don't want to take advantage, but execute complex code.

    my $z; if ($x) {$z = "true\n"} else {$z = "false\n"}

    But for my taste it's better written as

    my $z = $x ? "true\n" : "false\n";

    Your trap is even documented in perlop#Conditional-Operator

    Because this operator produces an assignable result, using assignments without parentheses will get you in trouble. For example, this:

    $x % 2 ? $x += 10 : $x += 2

    Really means this:

    (($x % 2) ? ($x += 10) : $x) += 2

    Rather than this:

    ($x % 2) ? ($x += 10) : ($x += 2)

    That should probably be written more simply as:

    $x += ($x % 2) ? 10 : 2;

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    ¹) See also WP : "The conditional operator's most common usage is to make a terse simple conditional assignment statement. "

    ²) tho I don't think I ever took advantage of the lvalue aspect, to create a LHS

    DB<22> 1 ? $a : $b = 1 DB<23> x $a,$b 0 1 1 undef DB<24> 0 ? $a : $b = 2 DB<25> x $a,$b 0 1 1 2

    (I'd certainly use parentheses in productive code.)

Re: Evaluating the condition ($x)
by Fletch (Bishop) on Aug 26, 2024 at 03:29 UTC

    B::Deparse is maybe illuminating. In either case it looks like the LHS of the second assignment winds up being $z which is then assigned "false\n".

    $ perl -MO=Deparse,-p,-q -E '($x) ? $z = "true\n" : $z = "false\n"; +print $z;' use feature 'current_sub', 'bitwise', 'evalbytes', 'fc', 'isa', 'modul +e_true', 'postderef_qq', 'say', 'signatures', 'state', 'unicode_strin +gs', 'unicode_eval'; (($x ? ($z = "true\n") : $z) = "false\n"); print($z); -e syntax OK

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Evaluating the condition ($x)
by talexb (Chancellor) on Aug 26, 2024 at 16:27 UTC

    So, short answer: use more brackets. I sprinkle a fair number of brackets in my code, and I'm also generous with white space. It helps clarify the meaning of the code, and hopefully avoids situations like this.

    Longer answer: For your first example, I would write that as

    if ( $x ) { print "true\n"; } else { print "false\n"; }
    rather than use a bare ternary as you have done. A ternary is more useful if you're in the middle of a print statement, like this:
    print "The truth value of x is " . ( $x ? 'true' : 'false' ) . "\n";
    The ternary operator comes from C -- it's a great piece of shorthand.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: Evaluating the condition ($x ||= $y)
by harangzsolt33 (Deacon) on Aug 26, 2024 at 03:21 UTC
    Btw I can confirm that I get the same result on Windows XP TinyPerl 5.8.

    My suspicion is that the answer may lie in the precedence of operators. The = seems to be evaluated first, and then the the ?: group second. Am I right?

    We could confirm this by doing : $z = 1; $x = 1; $z = $x ? $z = 2 : $z *= 9; print $z;

    Now, the output is not 1, it's not 9 but 18. How does it become 18? The fact that it's 18 seems to suggest that first the $z = 2 gets evaluated, and then immediately after that the $z *= 9 gets evaluated. (Note: the = sign and *= are on the same level of precedence.)

    Edit: Another interesting code is $z = 1; print $z *= 2, $z *= 9; # This prints 1818 instead of 218

    Edit: I thought I was onto something, but apparently not. I'm just even more lost now. I did this :

    my $A = 1; my $B = 7; my $C = 5; my $D = 0; $D = $A ? $B *= 2 : $C *= 9; print "A=$A B=$B C=$C D=$D";

    And the output is..... :drum roll:

    A=1 B=126 C=5 D=126

    Okay. I give up. I'm lost! Maybe we discovered a bug? How does $B become 126? Lol

      Am I right?

      Yes, I think so.
      The problem is fixed by changing the troublesome rendition to:
      ($x) ? ($z = "true\n") : ($z = "false\n"); print $z;
      I sure am glad I generally go for the extra bit of typing and use if/else. Bloody hell ... the amount of time I can spend in a ternary op rabbit hole ... :-(
      Thanks harangzsolt33, Fletch, hv.

      Cheers,
      Rob
        say $x ? "true" : "false";

        And it's called the conditional operator. It is merely a ternary operator, just like addition operator is a binary operator. It not even Perl's only ternary operator.

        #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11161348 use warnings; my $x = '1'; my $z = "undetermined\n"; ($x) ? print "true\n" : print "false\n"; ($x) ? $z = "true\n" : $z = "false\n"; print $z; # fewer parens :) $x ? $z = "true\n" : ($z = "false\n"); print $z; # my preferred version $z = $x ? "true\n" : "false\n"; print $z;