Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

variable set to 0 ? 0 : 1

by c (Hermit)
on Sep 06, 2002 at 05:00 UTC ( [id://195572]=perlquestion: print w/replies, xml ) Need Help??

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

 return $status == 0 ? 0 : 1;

I read through return and I've used it in the past to return values from within a subroutine to the main script. However, I'm very confused by this snippet I am seeing in a script I've downloaded from the internet. I'm not sure what is going on with the values that $status is being set to. They're unquoted, so not a literal string, and I would have thought that question mark would have caused problems. Can someone explain just how that == is working or what its doing?

thanks! -c

Replies are listed 'Best First'.
Re: variable set to 0 ? 0 : 1
by Arien (Pilgrim) on Sep 06, 2002 at 05:11 UTC
    return $status == 0 ? 0 : 1;

    is just another way of writing

    if ($status == 0) { return 0; } else { return 1; }

    which makes more sense as this:

    return $status;

    or if you only want to return 1 and 0 instead of just "true" and "false":

    return $status ? 1 : 0;

    — Arien

        Nice golf, but your solution never returns 0, which is what the original snippet does. Furthermore, if $status is '0E0' or '0.0' it returns 1, because they both are true values, though numerically zero.
        See:
        for ( 0, 0.0, "0", "0.0", "0E0", 1, "1" ) { print $_, "\t", !!$_, "\t", ( $_==0 ? 0 : 1 ), "\n"; }

        prints out
        0               0
        0               0
        0               0
        0.0     1       0
        0E0     1       0
        1       1       1
        1       1       1
        


        Best regards

        Antonio Bellezza

        Update: As pointed out in other posts, the same behaviour of "0.0" and "0E0" is true for strings not representing numbers, which have true values, but behave as 0 in numeric comparison. The difference is a warning of type
        Argument "xyz" isn't numeric in numeric eq (==) at - line 3.
        
        when -w is enabled.
        That's how I used to do it in C/C++ before it had a bool type, when it was necessary to canonize the value.

        If that's too idiomatic, using return asbool($status) where that's an inlined function would be readable and just as efficient.

        Note that it does a bit more than it does in a stronly-typed language. What if $status is not an int at all? The normal boolism is to treat undef and empty strings as false, also, and in one known case 0e1 is used as zero-but-true. The original test will be different from that; the !! idiom preserves it.

        —John

        How about
        return $status && 1; # canonize false, leave true as-is
        for completness sake.

        update: I got that backwards. && returns the last part evaluated, and skips the right side if the left already determines the outcome. So, if $status is false it returns it unchanged; if true it evaluates the right hand side and gets a numeric 1. It canonizes true, leaves false as-is.

        $status || 0; will return $status unchanged if true, and check the rhs if false. So that canonizes false and returns true unchanged.

        How about...

        return 0+($status!=0)

        Decent golf score and I think that works just like the original. Can anyone think of a counterexample?

        -sauoq
        "My two cents aren't worth a dime.";
        
      or if you only want to return 1 and 0 instead of just "true" and "false":
      return $status ? 1 : 0;

      I know this is a bit of a dead horse but someone should have pointed out that your code above is not equivalent to the original. Consider $status = "a string" for instance. The original would return zero because that "a string" == 0 is true. Yours would return 1 because "a string" is also true.

      -sauoq
      "My two cents aren't worth a dime.";
      
        Someone should have pointed out that your code above is not equivalent to the original.

        You are right. After explaining the exact meaning of the snippet, I just suggested a more sensible, although not exactly equivalent, way. :-)

        — Arien

Re: variable set to 0 ? 0 : 1
by dws (Chancellor) on Sep 06, 2002 at 05:12 UTC
    Can someone explain just how that == is working or what its doing?

    By operator precedence,   return $status == 0 ? 0 : 1; is treated as   return ($status == 0) ? 0 : 1; which is a stylistic way of saying "return 0 if $status is 0, otherwise return 1".

    Other ways to write this include

    if ( $status == 0 ) { return 0; } else { return 1; }
    which is what you might expect a junior C programmer to write, or
    return 0 if $status == 0; return 1;
    which you might see a Perl programmer write if they eschew the trinary operator.

Re: variable set to 0 ? 0 : 1
by Aristotle (Chancellor) on Sep 06, 2002 at 06:05 UTC

    The explanation has already been given exhaustively, but I want to expand a bit on semantics here.

    Returning zero in Perl is usually a bad meme. It is ok if you actually meant to return a numeric zero, but if you mean to return "false", the Perlish way is to return an empty list <update> in list context and undef otherwise. </update> This is because by returning 0 you return a single-element list, which evaluates to true in list context. The code would then look like this:

    return $status == 0 ? (wantarray ? () : undef) : 1;
    or maybe better
    return 1 if $status == 0;
    return; # a return by itself implicitly returns an empty list
    Another pitfall may or may not be the == 0. Is it meant to test falseness? If so, it is not precise. Perl understands the notion of an undefined value <update> and also considers a defined but empty string as well as a literal string "0" to be false </update>, which are not the same as zero, so this test is broken if it means to check falseness. In all likelihood, the programmer there should have written
    return 1 if $status; return;

    Updated: thanks ++Flexx. Moved one code example, added a few small but important bits.

    Makeshifts last the longest.

      Returning zero in Perl is usually a bad meme. It is ok if you actually meant to return a numeric zero, but if you mean to return "false", the Perlish way is to return an empty list. This is because by returning 0 you return a single-element list, which evaluates to true in list context. The code would then look like this:

      The opposite could be argued just as easily: if you return an empty list to mean "false", the false-ish-ness could be lost (or other problems could arise) if your return value is "slurped" into another list context.

      Consider...

      #!/usr/local/bin/perl -wl use strict; sub a_func_that_returns_false { return 0; } sub your_func_that_returns_false { return (); } sub func_that_expects_string_boolean_and_num { my $str = shift; my $bool = shift; my $num = shift; print "CALLED FUNC: ", $str; print " bool was ", ($bool ? "true" : "false"); print " num was ", (20 < $num ? "bigger" : "smaller"), " then tw +enty"; } &func_that_expects_string_boolean_and_num ("the zero way", &a_func_that_returns_false(), 42); &func_that_expects_string_boolean_and_num ("the empty list way", &your_func_that_returns_false(), 42); __DATA__ laptop:~> monk.pl CALLED FUNC: the zero way bool was false num was bigger then twenty CALLED FUNC: the empty list way bool was true Use of uninitialized value in numeric lt (<) at monk.pl line 12. num was smaller then twenty

      Bottom line: there's no substite for using wantarray to check your calling context, and acting appropriately (except perhaps extensively documenting that your method may not work the way people think and making it their responsibility to force the calling context)

      Update: good point blakem

        there's no substite for using wantarray to check your calling context
        Interesting example, though I fail to see how wantarray will help in this case:
        sub use_wantarray { return wantarray ? () : 0 }
        Wont work properly in your example either.... Note that the above sub is equivalent* to the recommended way to return false, i.e. simply return:
        sub recommended_way { return; }
        * except when used in void context, but that isn't relevant here.

        -Blake

      ... if you mean to return "false", the Perlish way is to return an empty list. This is because by returning 0 you return a single-element list, which evaluates to true in list context.

      I feel the risk of that happening is mildly exaggerated. :-)

      If you directly evaluate the return value of the sub for truth you supply the sub a Boolean context, and there will be no problem.

      To run into problems, you'd have to call the sub in a list context, save the list of 1 return value and then evaluate that in scalar context. But then the problem would be evaluating a list in scalar context when you didn't mean to.

      — Arien

        The risk of that happening may be small, but it's a question of principle and good habit. Specifically, a question of principle of least surprise. If you simply return when you mean to return "false", your code will do the right thing in most cases without requiring hoop jumps on the part of the caller. There are obviously reasons to deviate from the standard idiom in specific cases, but you should be aware what the standard idiom is and why it is that; so that you know when and when not to apply it.

        Makeshifts last the longest.

      Another pitfall may or may not be the == 0. Is it meant to test falseness? If so, it is not precise. Perl understands the notion of an undefined value, which is not the same as zero, so this test is broken if it means to check falseness.

      Well, accutally.... it's true that undefined is not the "same" as zero, but they are numericly equal. As you can see, it generates a warning, but it does evaluate to true...

      laptop:~> perl -wle 'print "TrUe" if undef == 0' Use of uninitialized value in numeric eq (==) at -e line 1. TrUe

      A better example as to why you wouldn't want to use "$status == 0" as a test for truth is non numeric strings. the string "foo" is true, but it is numericly equal to 0...

      laptop:~> perl -le 'print "TrUe" if "foo" == 0' TrUe laptop:~> perl -le 'print "TrUe" if "foo"' TrUe
      return 1 if $status == 0; return; # a return by itself implicitly returns an empty list

      It's true that return by itself returns an empty list in list context. But it returns undef in scalar context -- not   scalar( () )! ;)

      Updated: Simply added a quote of what I was refering to, since the thread grew quite long. Improved punctuation to boost readability. Removed unneccesary extra link to return.

        Excellent catch; I updated my node.

        Makeshifts last the longest.

      You'll love this. If you return -0.0 then it's false and equal to zero unless you compared it to a string at some time and then it becomes true and equal to zero.

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: variable set to 0 ? 0 : 1
by Zaxo (Archbishop) on Sep 06, 2002 at 05:14 UTC

    That is the trinary operator ?:. It parses like this: return (($status == 0) ? 0 : 1);. The first of the terms is evaluated in bool context. If true, the whole trinary takes the value of the expression after the question mark. If false, it takes the value after the colon.

    If $status is an instance of a class with overloaded operators, it may not return a simple value. Or else it may be a floating point value, with the accompanying accuracy problems in comparisons. This expression forces $status into numeric context and converts any non-zero $status to 1. The returned value is a perl numeric type, whatever $status may be.

    Update: By the way, your title suggests you misread the == operator as assignment. It's numeric comparison for equality, instead. Minor clarification in text.

    After Compline,
    Zaxo

Re: variable set to 0 ? 0 : 1
by theorbtwo (Prior) on Sep 06, 2002 at 05:15 UTC

    This statement will return 0 if the $status variable is zero, or 1 if it is not. I'm not sure if you don't understand the precidence issues here, or if you don't understand what the ?: operator is. If it's the former, B::Deparse is often good for answering these sorts of questions; the correct precidence is return((($status == 0) ? 0 : 1));. I show how to derive this in Re: Ternary Operator: Condition-Negation.

    If it's the later, I suggest reading about the Conditional Operator in perlop. In short, the conditional operator (also called the ternary operator, since it's the only three-valued operator in common (programming) use) is a strange form of if, written as conditional ? if-true : if-false. If conditional is true, the value of the operator is if-true, otherwise the value is if-false.

    In the spirit of TIMTOWTID, these stanzas are all (nearly) equivlent:

    return $status == 0 ? 0 : 1; return !!$status; # Checks if $status is true or not, not if it is e +xactly zero; "" or undef are false, but not == 0. if ($status == 0) { return 0; } else { return 1; }

    Probably, in the code $status ends up being a count, but the implementor only wanted to gaurntee that if $status is nonzero, a true value is returned, so they could change the function to do less work in the future.

    Update: fixed typo. Thanks, blyman.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by responing to this node).

Re: variable set to 0 ? 0 : 1
by talexb (Chancellor) on Sep 06, 2002 at 13:02 UTC
    Well. This is a pet peeve of mine.

    Why do programmers try to do their job with as few brackets as possible? I would much rather write that as

    return ( ( $status == 0 ) ? 0 : 1 );
    To show that we are
    • Testing status against zero, and
    • Returning 0 if the test is successful, otherwise returning 1
    Use more brackets and the explanation is much easier!

    To put that into English, this returns 0 if the status value is 0; for all other values of status, it returns 1. So you could paraphrase this as "Did the routine return a non-zero status?"

    --t. alex
    but my friends call me T.

      Because lots of brackets (parens to those of us on this side of the puddle) often make code harder to read even if they make it easier to explain. I would venture a guess that most veteran programmers aren't spending the majority of their time writing code that is meant to be read by novices.

      They also make it more difficult to write. Your example, with two sets of parens, requires 8 more keystrokes if you count the shifts. That could add up to 12 inches or more of fingertip movement. When you write a lot of code that can be a very real concern.

      I'm not at all suggesting that one should write the leanest code possible, and I want to make that clear. A good rule of thumb is that you should write your code so that someone with skills comparable to your own will be able to easily read it. Consistency in your style will help you achieve that more than anything else. As our style evolves, our code generally becomes less cluttered as a matter of course.

      Of the variations below, all of which do the same thing, I prefer the last two.

      return ( ( $status == 0 ) ? 0 : 1 ); # Too cluttered. return ( $status == 0 ) ? 0 : 1; # Misleading. return ( $status == 0 ? 0 : 1 ); # Clear return $status == 0 ? 0 : 1; # Clean and clear.
      -sauoq
      "My two cents aren't worth a dime.";
      
        Well, I think we'll have to agree to disagree on this one.

        The point of formatting one's code is to make the meaning clear to the reader. This reader finds:

        return ( ( $status == 0 ) ? 0 : 1 ); # Readable, clear. return $status == 0 ? 0 : 1; # Confusing. Precedence?
        I *think* I know what the second line does, but I'm not 100% sure. Blame my 15 years of programming in C, but I have to use brackets when I write a return statement. If that means I'm writing C in Perl, so be it.

        I also like to put brackets around a conditional, to highlight that there's a logic test going on. I know that operator precedence will take care of it without brackets, but why strain my brain.

        Really, this is a tempest in a teapot. My Dad had a wonderful sentence that he claimed made sense, if only it were punctuated correctly: Smith, where Jones had had had had had had had had had had had the examiner's approval. Perfectly understandable: no punctuation is necessary, is it?

        --t. alex
        but my friends call me T.

        I agree with sauoq.

        But here's how I usually write the ternary operator when clarity is a goal:

        return $status == 0 ? 0 : 1;
        Yes, it seems unnecessarily expansive for this simple case... but I probably wouldn't do it for this simple case.

        I would, and do, do this for situations such as:

        return @things = $obj->get_current_list_of_things() ? grep /$wanted/, @things : $obj->default_things; # o.k., it's a contrived example. gimme a break.
        Predicate and two lemmas, each on its own line.
Re: variable set to 0 ? 0 : 1
by zakzebrowski (Curate) on Sep 06, 2002 at 12:57 UTC
    <shameless_plug>For more fun with ?, see this node, my first (and so far only) obfuscated attempt...</shameless_plug>

    ----
    Zak
    "There is no room in this country for hyphenated Americanism" ~ Theodore Roosevelt (1915)
Re: variable set to 0 ? 0 : 1
by Wally Hartshorn (Hermit) on Sep 06, 2002 at 19:43 UTC
    Since I don't think anyone has mentioned it, I just thought I would pop in here and emphasize that this is not a feature of the "return" statement. A generic example might be:
    $vacation_days = ($seniority > 5) ? 15 : 10;
    which is equivalent to:
    if ($seniority > 5) { $vacation_days = 15; } else { $vacation_days = 10; }
Re: variable set to 0 ? 0 : 1
by nmerriweather (Friar) on Sep 08, 2002 at 01:30 UTC
    i've never used the trinary operator before -- most of my subs w/returns give back a #, not a true/false

    i understand its use for return statements, but am a little unclear about other ways to use it (perhaps changing 1/0 to 'true'/'false' or some other manipulation of a variable?)

    i was hoping some people would be willing to share some other examples/samples/instances using this operator is good/appropriate for

      One interesting use that is (as far as I know) unique to Perl, is using the trinary as an Lvalue. That is on the left hand-side of an assignment as in this (rather silly) example.

      #! perl -sw use strict; my (@aboveC, @cOrLess); while (<DATA>) { # $1 := name, $2 := grade m/^(\w+)\s+([a-f][+-]?)$/i; # if the grade is one of these 0<= index('A+ A A- B+ B B- C+', uc($2), 0 ) # add name here if not add name here ? $aboveC[@aboveC] : $cOrLess[@cOrLess] = $1; } print "Above C students: @aboveC\n"; print "C or Less students: @cOrLess\n"; __DATA__ homer f bart d- marge c+ lisa a

      which gives

      C:\test>195952 Above C students: marge lisa C or Less students: homer bart C:\test>

      Well It's better than the Abottoire, but Yorkshire!
        I have to admit being excited about this construct the first time I saw it, though I don't know if I've ever really used it....
        push(@{ $whichone ? \@arr1 : \@arr2 }, $element);

        -Blake

      I usually use it to visually unclutter code that would otherwise be an if with tiny blocks. It can be used in void context for something like
      $i == 15 ? push @str, $_ : $j = $i += 1;
      or you can "chain" ternary operators by using another ternary as the false-branch of the previous:
      my $res = $cmd eq "add" ? $x + $y : $cmd eq "sub" ? $x - $y : $cmd eq "mul" ? $x * $y : $cmd eq "div" ? $x / $y : undef;
      which would otherwise have to be a hideously ugly and thrice as repetitive thing like this:
      my $res; if ($cmd eq "add") { $res = $x + $y } elsif($cmd eq "sub") { $res = $x - $y } elsif($cmd eq "mul") { $res = $x * $y } elsif($cmd eq "div") { $res = $x / $y } else { $res = undef }

      Makeshifts last the longest.

      Set $max to the maximum of $x vs $y:
      $max = $x > $y ? $x : $y;

      -Blake

Re: variable set to 0 ? 0 : 1
by somniculosus (Initiate) on Sep 08, 2002 at 14:36 UTC
    it does not set $status to 0, it merely tests whether status is *equal to* zero (remember == tests for numeric equality) and returns 0 if yes, and 1 if not. here, a lengthier version:
    if ($status == 0) { return 0; } else { return 1; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://195572]
Approved by weini
Front-paged by ignatz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (8)
As of 2024-04-18 16:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found