Troubleshooters.Com and Code Corner Present

Litt's Lua Laboratory:

Lua If Statements
(With Snippets)

Copyright (C) 2011 by Steve Litt

See the Troubleshooters.Com Bookstore.

Contents

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.