Troubleshooters.Com and Code Corner Present

Litt's Lua Laboratory:
Lua If Statements
(With Snippets)

Copyright (C) 2011 by Steve Litt



Debug like a Ninja

Contents

  • Introduction
  • If Statement Basics
  • Relational Operators
  • Else and Elseif
  • Local Variables Within If Statements
  • Short Circuit Logic
  • Introduction

    Every computer language has if statements. That's the main way to implement branching. Branching is the jumping to different code depending on some variable or condition.

    If Statement Basics

    Lua if statements are pretty simple. The simplest look like this:
    if boolean_expression_evaluates_true then
    do_this_code()
    end
    So only if the boolean expression evaluates true do you do the code. The words if, then and end are keywords. Words if and then delineate the condition to be evaluated for truth or falsehood, while words then and end delineate the code to be run if the condition is true, assuming there are no else or elseif keywords, which are discussed later.

    Remember there are only two values in the Lua world that are false: boolean false and nil. Any other value evaluates true.

    So what is a boolean expression? It could be almost anything, but here are a few:

    Relational Operators


    ==
    Equal
    ~=
    Not equal
    <=
    Less than or equal
    >=
    Greater than or equal
    <
    Less than
    >
    Greater than

    Operator Precedence
    From lowest to highest
    (from loosest to tightest binding)
    or
    Logical or:  Loosest binding
    and
    Logical and
    <     >     <=    >=    ~=    ==
    Relational operators
    less than, greater than, less than or equal,
    greater than or equal, not equal, equal
    ..
    String concatenation
    +     -
    Arithemetic add and subtract
    *     /
    Arithmetic multiply and divide
    not   - (unary)
    Boolean not and arithmetic unary minus
    ^
    Exponentiation: tightest binding

    The way you evaluate an expression is to start at the left and read to the right, and imagine parentheses around the things with tighter precedence. For instance:
    a * 2 + 3 < a + 3 * 4 or a < 25 and a < a * 5 - 4

    Original expression
    (a * 2) + 3 < a + (3 * 4) or a < 25 and a < (a * 5) - 4
    * is highest precedence, tightest binding in the expression, so start by putting parentheses around all *
    ((a * 2) + 3) < (a + (3 * 4)) or a < 25 and a < ((a * 5) - 4)
    + and - are next highest precidence, so put parentheses around them
    (((a * 2) + 3) < (a + (3 * 4))) or (a < 25) and (a < ((a * 5) - 4))
    Relationals are the next highest priority, so enclose them in parentheses
    (((a * 2) + 3) < (a + (3 * 4))) or ((a < 25) and (a < ((a * 5) - 4)))
    And is the next highest, so enclose both sides of the and in parentheses
    ((((a * 2) + 3) < (a + (3 * 4))) or ((a < 25) and (a < ((a * 5) - 4))))  
    Last but not least, well, actually yes the least, is or, so put parentheses around both sides of the or. This last set of parentheses enclose the entire expression, so you know you're done.
     
    Precedence is important. Run this program:
    #!/usr/bin/lua 
    print(true or false and true and false) -- prints true
    print(true or ((false and true) and false)) -- means same as above, prints true
    print((true or false) and (true and false)) -- prints false
    The first statement above uses precedence to decide what to do next. The second statement uses parentheses to explicitly direct Lua to do the same thing as precedence would make it do. The third statement strongarms it away from what precedence would normally indicate, and changes the result.

    Unlike C, the combination of Lua precedence rules plus Lua language rules make it likely that you can do what you want without parentheses, but they're always available in case you need them.

    Else and Elseif

    A basic if statement takes action only if something is true. But what if you want to take different actions depending on its truth? Then you need an else:
    if boolean_expression_evaluates_true then
    do_this_code()
    else
    do_other_code()
    end
    What if you want to take different actions depending on several boolean statements. Or putting it another way, what if a variable can take on several different values and you want to perform different actions depending on the variable? Watch this:
    if contribution < 20 then
    membergroup = "Fan"
    elseif contribution < 100 then
    membergroup = "Supporter"
    elseif contribution < 500 then
    membergroup = "Silver"
    elseif contribution < 2000 then
    membergroup = "Gold"
    elseif contribution < 5000 then
    membergroup = "Platinum"
    else
    membergroup = "Cornerstone"
    end
    Occasionally you might have elseif without else. For instance:
    if breed = "collie" then
    print "Big and fluffy"
    elseif breed = "greyhound" then
    print "Fast"
    elseif breed = "pug" then
    print "Cute"
    end
    In the preceding, you want to take action for collies, greyhounds and pugs, and take no action on other dogs.

    If you're anything like me, you code if statements in a paranoid way. For instance:
    if a < b then
    return -1
    elseif a == b then
    return 0
    elseif a > b then
    return 1
    else
    io.stderr:write("Internal error, aborting!")
    os.exit(1)
    end
    Now both you and I know  that a must either be less, equal or more than b, any other situation is clearly impossible, so it's kind of silly to include that last else clause. That's nice, but let the programmer who has never come across an impossible situation cast the first stone. Personally, I often find value in coding for the impossible for the same reason my keyboard has a backspace key -- I sometimes make mistakes.

    Local Variables Within If Statements

    Consider the following extremely contrived code:

    if contribution < 20 then
    local membergroup1 = "Fan"
    return membergroup1
    elseif contribution < 100 then
    local membergroup2 = "Supporter"
    return membergroup2
    elseif contribution < 500 then
    local membergroup3 = "Silver"
    return membergroup3
    elseif contribution < 2000 then
    local membergroup4 = "Gold"
    return membergroup4
    elseif contribution < 5000 then
    local membergroup5 = "Platinum"
    return membergroup5
    else
    local membergroup6 = "Cornerstone"
    return membergroup6
    end
    In the preceding code, membergroup3 was in scope only from its declaration until elsif contribution < 2000 then. In general, you figure out your local's scope by finding the smallest block bordered on top by then, do, elseif, else or repeat, and bordered on the bottom by a matching end, elseif, else or until. If there are contained blocks between the local's declaration and the end of its block, it's visible in those contained blocks.

    Short Circuit Logic

    Within the rules of precedence, Boolean statements execute left to right until the result is inevitable. The fact that evaluation stops when the result is inevitable enables you to do some fancy programming called short circuit logic, sometimes called short circuit evaluation. The following is a simple and classic use of short circuit logic:
    local myname = tempname or "default"
    In the preceding, if tempname contains a string, you assign the tempname to myname, and evaluate the whole expression. Because the whole expression is a single or, tempname is true unless it is nil, and if tempname is true then obviously the whole or clause is true, and evaluation stops. But if tempname is nil then it evaluates false, the value of the whole expression is still in doubt, so the "default" is evaluated and passed on to myname. This is an often used idiom to handle defaults.

    Now here's one with hair and teeth:
    local handle = io.open("junk.jnk", "r") or not print("open failed") and os.exit(1)
    There are better ways to do the preceding -- it's just being used as an example of how short circuit logic can be used to actually perform actions.

    The first thing that's done is the io.open(), and it can succeed or fail. If it succeeds it returns a file handle. If it fails it returns nil.

    If it returns nil, then the entire expression, which is one big  or with  an embedded and on the right, is in doubt, and will be until the embedded and on the right is evaluated.

    So now you go to evaluate the and statement. The print command always succeeds (unless perhaps there's some rare, horrible and unpredictable problem), but it's preceded by not to make it false, so the and is still in doubt and evaluation continues to the os.exit(1), which exits the program.

    So the whole expression either gets a handle for file junk.jnk or prints an error message and exits the program. Obviously you'd be better off using the assert() command or explicit code with an if statement. This code was contrived just to demonstrate use of short circuit logic to do work rather than just to set a variable.


     [ Troubleshooters.com| Code Corner | Email Steve Litt ]

    Copyright (C) 2011 by Steve Litt --Legal