Control Structure Tutorial

lua-users home
wiki

Control structures let your program make choices, or to run the same piece of code many times.

if statement

The if statement lets you run different code based on a condition:

if condition then
  block
elseif condition2 then
  block
elseif condition3 then
  block
else
  block
end

The if and elseif parts are checked in order, and once one of the conditions is true, it runs the block under it and skips to the end, ignoring any other elseif conditions after it. The else block is run if none of the conditions match. Finally, the elseif and else parts are optional.

> n = 5
> if n > 5 then print("greater than 5") else print("less than 5") end
less than 5
> n = 7
> if n > 5 then print("greater than 5") else print("less than 5") end
greater than 5

A more complex example:

> n = 12
> if n > 15 then
>> print("the number is > 15")
>> elseif n > 10 then
>> print("the number is > 10")
>> elseif n > 5 then
>> print("the number is > 5")
>> else
>> print("the number is <= 5")
>> end
the number is > 10

Notice how just one of the messages is printed, even though more than one of the conditions are true: This is because once one matches, the if statement skips checking the other conditions.

while loop

while condition do
  block
end

This runs the block over and over in a loop, but on each iteration, it first checks the condition, and if it's false, skips to the end, breaking the loop. If the condition is always false, the block will never be run.

> i = 1
> while i <= 10 do
>> print(i)
>> i = i + 1
>> end
1
2
3
4
5
6
7
8
9
10

repeat loop

repeat
  block
until condition

Same as the while loop, except the condition is inverted (breaks the loop when true), and it's checked after the first iteration, so the code is guaranteed to run at least once.

> i = 5
> repeat
>> print(i)
>> i = i - 1
>> until i == 0
5
4
3
2
1

numeric for loop

for variable = start, stop, step do
  block
end

Runs the block with variable first being equal to start, then keeps incrementing it step amount and running the block again until it's greater than stop. step can be omitted and will default to 1.

You can also make the step negative, and the loop will stop once the counter variable is less than the stop value.

> for i = 1, 5 do
>> print(i)
>> end
1
2
3
4
5
> for i = 1, 100, 8 do
>> print(i)
>> end
1
9
17
25
33
41
49
57
65
73
81
89
97
> for i = 3, -3, -1 do
>> print(i)
>> end
3
2
1
0
-1
-2
-3
> for i = 0, 1, 0.25 do
>> print(i)
>> end
0
0.25
0.5
0.75
1
> for i = 1, 3 do
>> for j = 1, i do
>> print(j)
>> end
>> end
1
1
2
1
2
3

Also remember that the variable in a for loop is only visible inside the block, it will not still be there containing the last value after the loop is broken.

iterator for loop

for var1, var2, var3 in iterator do
  block
end

The iterator version of the for loop takes a special iterator function, and can have any amount of variables. What the loop does, how many variables it needs, and what they will be set to depends on the iterator.

This is mainly good for tables, which haven't been introduced yet, but here's an example to give you an idea:

> tbl = {"a", "b", "c"}
> for key, value in ipairs(tbl) do
>> print(key, value)
>> end
1       a
2       b
3       c

Here, ipairs is the iterator, which gets the numbered entries from a table in order.

break statement

The break statement causes Lua to jump out of the current loop:

> i = 3
> while true do -- infinite loop
>> print(i)
>> i = i + 1
>> if i > 6 then
>> break
>> end
>> end
3
4
5
6

With nested loops, break only affects the innermost one:

> for i = 1, 2 do
>> while true do
>> break
>> end
>> print(i)
>> end
1
2

Using break outside of a loop is a syntax error:

> break
stdin:1: <break> at line 1 not inside a loop

continue statement alternatives

Many other languages have a continue statement that skips the rest of the current iteration of the innermost loop. In Lua 5.2, this can be imitated using goto:

> for i = 1, 10 do
>> if i>3 and i<6 then goto continue end
>> print(i)
>> ::continue:: -- a name surrounded in :: :: is a goto label
>> end
1
2
3
6
7
8
9
10

Lua 5.1 and earlier doesn't have goto, but there are other workarounds:

> for i = 1, 10 do
>> if not (i>3 and i<6) then
>> print(i)
>> end
>> end
1
2
3
6
7
8
9
10
> for i = 1, 10 do repeat
>> if i>3 and i<6 then break end
>> print(i)
>> until true end
1
2
3
6
7
8
9
10

Conditions

Conditions don't necessarily have to be boolean values. In fact, any value is a valid condition: nil and false make the condition false, anything else (including 0) makes it true.

> if 5 then print("true") else print("false") end
true
> if 0 then print("true") else print("false") end
true
> if true then print("true") else print("false") end
true
> if {} then print("true") else print("false") end
true
> if "string" then print("true") else print("false") end
true
> if nil then print("true") else print("false") end
false
> if false then print("true") else print("false") end
false

Also there are some languages where variable assignment is considered an expression (so it can be used as a sub-expression), so code like this is written:

> i = 0
> while (i = i + 1) <= 10 do print(i) end
stdin:1: ')' expected near '='
But in Lua assignment is a statement, and the above example is a syntax error.

if/else as an expression

Some languages have a ternary operator that acts like an if/else statement, but can be used as a sub-expression. If the condition is true, it evaluates to one expression, otherwise it evaluates the other.

Lua doesn't have such an operator, but in many cases you can imitate it using the and and or logical operators. This works for two reasons: those operators don't even run the right-side expression if the logical result is known only from the left side result, and also they directly return the result of their sub-expressions, instead of converting them to boolean:

> = true and print("test")
test
nil
> = false and print("test") -- 'and' is always false if one of the sides are false, don't bother running the other expression
false
> = true or print("test") -- 'or' is always true if one of the sides are true, don't bother running the other expression
true
> = false or print("test")
test
nil
> = 8 or 5
8
> = true and "text"
text

This can be used to make a simple if/else expression:

> condition = true
> = condition and 2 or 4
2
> condition = false
> = condition and 2 or 4
4
> = condition and print("a") or print("b") -- only the "false" branch is run, otherwise both a and b would be printed
b
nil

Remember that "and" has a higher precedence than "or": If the condition is false, it makes the "and" expression give up and return false. This then makes the "or" part try its right-side expression and return its value. If the condition is true, "and" will return its right-side expression. This is then given to "or", which sees that the left result is a true condition, and just returns it.

Note that we assumed that the result of the true branch is a value that acts as a true condition. This leads to the catch: the true branch cannot evaluate to nil or false, because then the false branch will also be run and its value will be returned:

> condition = true
> = condition and false or true -- wrong result!
true

This is because the whole "and" sub-expression is now false, causing "or" to try running its other sub-expression. But it is alright to return a false value from the false branch. In fact, if you're in a situation like the above example, you can just invert the condition and swap the contents of the branches:

> condition = true
> = not condition and true or false
false

If both branches must return a value that acts as a false condition, there's one way to work with 'or true' operator.

> condition = 1
> =condition >= 1 and (print("condition >= 1") or true) or print("condition < 1")
condition >= 1
true

RecentChanges · preferences
edit · history
Last edited June 5, 2023 12:03 pm GMT (diff)