Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

if variants

by apotheon (Deacon)
on Oct 19, 2004 at 09:28 UTC ( [id://400446]=perlmeditation: print w/replies, xml ) Need Help??

The if statement is known well to all but the most inexperienced Perl newbie. That's not pejorative: I was nearly that inexperienced myself when I wrote this, and did so as a means of self-education.

There's more to if statements than immediately meets the eye, though. They are control structures — ordered segments of algorithm — that can take several forms and be used in a number of different ways. They are, in short, boolean conditionals: either/or decisions leading to execution of further instructions.


if

The first thing the most basic if statement does is test for the truth of an expression. That expression is the "control statement" of the if statement. When the expression is evaluated as true, the code in the statement block is executed: when it is evaluated as false, the code in the statement block is ignored. Example:
if ($foo == 7) { $foo++; }

In the above example, the control statement expression is $foo == 7. If the contents of the scalar $foo are equal to 7, it evaluates as true, and the code within the statement block (delimited by the braces) is executed. If the contents of the scalar $foo are equal to some other number or string, or $foo is undefined, the code within that statement block is not executed. Thus:
#!/usr/bin/perl -w use strict; my $foo; $foo = 7; if ($foo == 7) { $foo++; } print($foo);

In the above example, executing the program simply causes the numeral 8 to be printed to STDOUT, which is normally the screen. (Typically you'd want to include a newline in what you print to STDOUT in a program like this, but it's not relevant to the example.) Meanwhile, in the following example, the STDOUT output is the numeral 6, because the increment operation $foo++ is not executed when the control statement is evaluated as false:
#!/usr/bin/perl -w use strict; my $foo; $foo = 6; if ($foo == 7) { $foo++; } print($foo);

That, of course, is all very basic. It gets slightly more interesting from here.


unless

The unless statement is a reversal of the control statement in an if statement. For instance, where above the control statement $foo == 7 causes the if statement block to be executed if evaluated as true, the code in the unless statement block is executed only if the control statement is evaluated as false. Thus, unless ($foo == 7) achieves exactly the same result as if ($foo != 7). The same is true, when working with strings, of the eq and ne operators.


if, reversed

Another way to represent an if statement is to use a one-line reversal of the normal order of it. Just as in English one can say both "If this happens, that follows," and "that follows if this happens," so too can you switch the order of expressions in an if statement. The above if statement that increments the value of $foo when the variable evaluates as true can be written as follows:
$foo++ if $foo == 7;

Clearly, using that reversed syntax can be an incredible saver of time and space when writing code. One must be aware of its rules and limitations, however. First of all, its elements are presented in an order opposite the usual method of representing the if conditional and statement block, and secondly you can (in general: there are ways around this) only execute one expression if the control statement evaluates as true. The longer form of the standard if statement allows for multiple instructions to be executed, while the shorter form only allows a single instruction to be executed.


unless, reversed

The same can be done with an unless statement as with an if statement. For instance, executing $foo++ in the event that $foo == 7 proves false would be represented by this unless statement:
$foo++ unless $foo == 7;


&&/|| == if/unless reversed

An even shorter, simpler way to represent both of the above control structures exists:
$foo++ if $foo == 7;
is the same as
$foo == 7 && $foo++;

and
$foo++ unless $foo == 7;
is the same as
$foo == 7 || $foo++;

The reason these last variants on the if and unless statements work requires some understanding of formal logical constructions beyond that of the more traditional forms of conditionals. The && operator works as an if conditional because of the manner in which it is applied by the interpreter:
  1. If $foo == 7 is true, then the value of the entire "and" expression is unknown. It cannot be evaluated as true or false as a whole until the part following the && operator is evaluated. The $foo++ portion is then executed and, because it's executed without fail, the complete expression $foo == 7 && $foo++; evaluates as true.

  2. If $foo == 7 is false, then the value of the second part of the entire statement ($foo++) is irrelevant because the whole statement can only be evaluated as true if both sides are true. The code for that expression stops before executing the increment operation.

Much the same occurs with the unless statement using the || operator. I leave the details of this as an exercise for the reader.


Appendix A: operator precedence

It's worth noting that operator precedence is important in using the && and || operators to create if and unless statements. In general, anything of higher precedence than the && or || operator in your statement is safe to use in your if statement or unless statement. If a lower-precedence operator is used, it can cause parts of your expressions that make up (for instance) the control statement to be attached to a new expression involving the && or || operator, rather than being used as you intended.

All standard arithmetic operators and comparison operators are of higher precedence than && and ||. Assignment operators are lower precedence. The and and or operators perform precisely the same functions as && and ||, respectively, but have a much lower precedence. This lower precedence causes them to be executed after even assignment operators, commas, and list operators.

Much of this confusion over precedence can be avoided, of course, by simply using parentheses liberally (and properly). There are two somewhat contradictory schools of thought regarding the use of parentheses to CYA when dealing with operator precedence: one is that you should use parentheses often and with enthusiasm to ensure proper code execution, and the other is that you should always ensure that you know the proper operator precedence and avoid parentheses where possible. On one hand, it's true that diligent writing of code with avoidance of parentheses makes for a cleaner program and is, perhaps, a sign of some competence. On the other hand, using parentheses even where they might sometimes be avoided can aid in ease of parsing code by eye and maintaining code later. Ultimately, decisions on the matter must be made by the programmer.


Appendix B: more actions in one-line if statements

As I said in if, reversed above, using one-line formats for the if and unless statement limits the programmer to only one executed statement when the control statement is evaluated as true (or, in the case of unless, evaluated as false). I also said there are ways around it, though. I'll use if, and not unless, for my examples.

For one thing, even the standard format for an if statement can be represented on one line. Thus, if ($foo == 7){$foo++;} does the same thing as the following:
if ($foo == 7) { $foo++; }

That's probably a bad idea, however. Using a statement block with braces around it allows for the flexibility of adding and subtracting statements within it, and collapsing it into one line renders the code less easy to read and maintain later without even making the code more succinct. The only keystrokes you really save involve your right pinky. The other methods of creating one-line if statements I mentioned above are recommended where they're appropriate.

Another means of expanding the functionality of the one-line if statement is to use the and statement as described in Appendix A above, which allows you to list multiple expressions for execution. For instance, you could do the following:
$foo == 7 and $foo++, $bar--;

You can also achieve much the same result through reliance on operator precendence. For instance, you could use this:
$foo == 7 && $foo++ && $bar++;

Other, innumerable exceptions apply to the one-line if statement's limitations — far more than I can get into in this brief treatment of the subject.


Thanks due the PerlMonks community members who contributed suggestions and comments in discussion below.


- apotheon
CopyWrite Chad Perrin

Replies are listed 'Best First'.
Re: if variants
by Chady (Priest) on Oct 19, 2004 at 11:53 UTC

    regarding the precedence, you can also use the and and or variants who have lower precendence so that you can have assignements work.

    Here's a rough example with nested ifs and an else

    if ($i > 1 ) { if ($a = function_that_may_fail()) { $i++; } else { $i = 1; } }

    can become:

    $i > 1 and $a = function_that_may_fail() and $i++ or $i = 1;

    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.
    Chady | http://chady.net/
    Are you a Linux user in Lebanon? join the Lebanese Linux User Group.
      Personally I like:

      if ($i>1) { $a = function_that_may_fail(); $i = ($a) ? $i+1 : 1; }

      I find that the only time I really use "and" and "or" are with carp/croak/warn/die. But that's just me.

      At worst:

      do { $a=function_that_may_fail(); $i= $a ? $i+1 : 1; } if ($i>1)
      Ack! I forgot to mention parentheses! I've got to go add slightly to my "Appendix" section, now.

      - apotheon
      CopyWrite Chad Perrin
Re: if variants
by itub (Priest) on Oct 19, 2004 at 12:31 UTC
    Note that you can do more than one thing on a "one-line if" by using the comma operator:

    $state = 3, next if $tok eq 'a';

      or more generally, with a CODEREF

      sub{ MowLawn(); WashCar(); InvadePakistan();}->() if $bNotRaining;
      Or more generally, with a do statement:
      do {local($,) = (','); print(@array);} if $tok eq 'a';
Re: if variants
by demerphq (Chancellor) on Oct 19, 2004 at 19:27 UTC

    Since I havent seen this explicitly in your post i thought i should add that in many respects "if" is the same as "and".

    $foo==7 and $foo++;

    Likwise "or" is similar to "unless"

    $foo==7 or $foo++;

    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi

      Flux8


Re: if variants
by apotheon (Deacon) on Oct 19, 2004 at 18:17 UTC
    I've taken the comments pointing out methods of getting around the apparent limits on multiple actions in a one-line if statement to heart and inserted a brief mention into the text of if variants. Rather than actually attempt to comprehensively list and explain them all, adding cruft to what may already be a too-bloated treatment of if statement variants, I have simply made brief mention of the fact and left it at that.

    I think it would be helpful and useful, and generally a good idea, if someone were to create a new node on the topic of such exceptions to that limitation of one-line conditionals. If the author of such a thing messages me after creating that node, I'll have a look at it and consider linking to it from the text of this node (assuming it's sufficiently relevant and clearly written).

    Or, I might write it myself if nobody else does, eventually. I'm not a Perl guru, however, and that might be some time in coming.

    - apotheon
    CopyWrite Chad Perrin
      Im not sure having a heap of little snippets of info is really useful here. Probably better to integrate such suggestions into your text, especially if it was meant as a Tutorial. (Was it, if so please indicate somewhere, so we can link it on the Tutorials page).

      It's ok to post a work in progress, have people comment, suggest additions, and then update. That way, the info is all in one page, and not scattered around in several.

      C.

        I'm already in the business of adding appendices to this node, apparently, so I'll add another on the subject of adding more actions to a single-line if statement. Thanks for the advice.

        If if variants is thought to be worth linking amongst the site's tutorials, I'd be happy to see it there.

        - apotheon
        CopyWrite Chad Perrin
Re: if variants
by ihb (Deacon) on Oct 20, 2004 at 01:01 UTC

    Using parentheses, rather than relying on precedence, should be habit anyway for a good programmer.

    I disagree! I disagree a lot! Knowing your precedence is a part of knowing the language. Sometimes, but just sometimes, adding parenthesis can add to clarity. Otherwise, I think they're just cluttering the code. You shouldn't put unnecessary "precedence parentheses" in Perl just as you don't do x + (y * z) when doing math. The desire to add parenthesis rather than to "rely on precedence" is usually a sign of uncertainty of the programmer who consequently does it "for safety". If you're not certain of the precedence for what you want to write, don't do yourself the disfavour of adding "safety parentheses", instead look in perlop. You'll soon learn the precedence well enough to not have to look there so often.

    I found the node to be unnecessary hard to read. It would benefit from some editorial updates. It would be great to have paragraph titles. For the code that is on its own line it would be good to have it either indented or surrounded by blank lines, or preferably both. Adding code tags consistantly for all code parts in the text, including && and variable names, would also make it easier to read.

    ihb

    See perltoc if you don't know which perldoc to read!
    Read argumentation in its context!

      I've taken your suggestions and comments under consideration, and made some edits. Thank you.

      - apotheon
      CopyWrite Chad Perrin
Re: if variants
by DrHyde (Prior) on Oct 20, 2004 at 09:21 UTC
    One thing that's worth mentioning is that while this:
    if($foo == 7) { print "foo is 7\n"; } else { print "foo is not 7\n"; }
    is acceptable, this:
    unless($foo != 7) { print "foo is 7\n"; } else { print "foo is not 7\n"; }
    is not. Sure, perl will accept it (I consider this to be a bug in perl), and it'll run, but anyone writing it should be beaten savagely. It's VERY hard to read.
      I edited to include some brief mention of the undesirability of the != operator there. Thanks for the suggestion.

      - apotheon
      CopyWrite Chad Perrin

        It's worth noting, by the way, that using $foo != 7 as the control statement in an if statement is generally a Bad Idea. The unless statement is designed to cover that need, and using the != operator there just makes the code harder to read later when it must be maintained.

        I second DrHyde (Re^3: if variants). It's definately not bad and neither is it a maintenance trouble. That whole sentence above should be removed from your text. It's just plain wrong.

        The unless keyword is made to make you write code more like a natural language. It's not there to take !='s place in if expressions. They can both coexist happily and be mixed however you please.

        Why the combination of unless and else is shunned by many is because it's a sort of double negation.

        if (X) { ... } else { ... }
        can be read
        if (X) { ... } if (not X) { ... }
        "unless X" can be read "if not X". Combining this gives you
        unless (X) { ... } else { ... } if (not X) { ... } if (not not X) { ... }
        which just doesn't read well. Logically it's not strange but it's a funny and inappreciated way of expressing oneself.

        My own (rather inconsistent) style is to use unless for exceptional behaviour, and if for expected/wanted behaviour. When using unless I usually want something, but something might stop me from doing that. With if I express that I perhaps want something, perhaps not.

        print ... unless $quiet; # I want to print. exit if $done; # I might want to exit here. exit unless $stay; # I want to exit here, but apparently # something is holding me back.
        The examples can perhaps be better, and I'm not consistant myself in my use, but that's give you an idea of how I tend to use them.

        ihb

        See perltoc if you don't know which perldoc to read!
        Read argumentation in its context!

        Noooooo! != is fine. It's unless ... else that is hideously broken!

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2024-03-19 05:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found