Lesson 3 • Beginner Track
Control Flow and Loops
By the end of this lesson you'll be able to make decisions with if/elseif/else, compare and combine values with Lua's word-based operators, and repeat work with every kind of Lua loop — while side-stepping the truthiness trap that catches developers from every other language.
-- prints: comments — run them at onecompiler.com/lua or with the local lua command to see for yourself.What You'll Learn
- Branch your program with if / elseif / else / end
- Compare values with ==, ~= (not !=), <, >, <=, >=
- Combine conditions with and / or / not and the 'and ... or' ternary idiom
- Repeat work with while, repeat...until, and the numeric for
- Walk tables with the generic for using pairs() and ipairs()
- Exit loops early with break — and know Lua has NO continue
local variables and printing with print(...). Everything else we build here.💡 Real-World Analogy
Think of control flow as the signs and roundabouts on a road trip. An if is a junction: "is the light red? then stop, else go." A loop is a roundabout you keep circling until a condition lets you exit — break is the exit ramp you take the moment you spot your turn. Your code, like a car, only ever drives ONE path at a time; the conditions decide which. Get the signs right and the journey runs itself; get one backwards and you loop forever.
1. Making Decisions with if / elseif / else
An if runs a block of code only when a condition is true. Add elseif branches to test more conditions, and a final else to catch everything left over. Lua checks the tests top to bottom and runs the first one that is true, then jumps to end. Two things to burn in now: the branch keyword is the single word elseif (not else if), and every if must be closed with end. Read this worked example, then you'll write your own.
-- An 'if' decides which code runs based on a true/false test.
-- Shape: if <condition> then ... elseif <condition> then ... else ... end
-- EVERY if block must be closed with the word 'end'.
local score = 85
if score >= 90 then
print("Grade: A — Excellent!") -- runs only when score >= 90
elseif score >= 80 then
print("Grade: B — Great job!") -- prints: Grade: B — Great job!
elseif score >= 70 then
print("Grade: C — Good effort")
else
print("Grade: F — Keep practising")
end
-- Note the single word 'elseif' — NOT 'else if' (that would need two 'end's).
-- Lua checks each test top to bottom and stops at the FIRST one that is true.2. Relational Operators (and the ~= surprise)
Conditions are usually built from relational operators that compare two values and return true or false. Most are familiar: >, <, >=, <=, and == for "equal to". The one to memorise is "not equal": in Lua it is ~= (tilde-equals), not !=. Writing != is a syntax error, so pair them in your head: equal is ==, not-equal is ~=.
-- Relational operators compare two values and give back true or false.
print(5 == 5) -- prints: true (== means "equal to")
print(5 ~= 4) -- prints: true (~= means "NOT equal to")
print(5 ~= 5) -- prints: false
print(7 > 3) -- prints: true
print(7 >= 7) -- prints: true
print(2 <= 1) -- prints: false
-- IMPORTANT: Lua writes "not equal" as ~= (tilde-equals).
-- It is NOT != — writing 5 != 4 is a SYNTAX ERROR in Lua.
-- == also compares strings character by character:
print("cat" == "cat") -- prints: true
print("cat" == "Cat") -- prints: false (case-sensitive)3. Combining Conditions: and / or / not
To join conditions, Lua uses words, not symbols: and, or, and not (there is no &&, ||, or !). and needs both sides true; or needs at least one; not flips a value. Lua also has no ternary operator, so the community idiom cond and a or b stands in for "if cond then a else b" — handy for picking one of two values on a single line.
-- Logical operators in Lua are WORDS: and, or, not (not && || !).
local age = 20
local hasID = true
print(age >= 18 and hasID) -- prints: true (both sides must be true)
print(age < 13 or age > 65) -- prints: false (needs at least one true)
print(not hasID) -- prints: false ('not' flips true/false)
-- Lua has NO ternary operator (no cond ? a : b ).
-- The idiom is: cond and a or b
local isAdmin = true
local role = isAdmin and "Admin" or "User"
print(role) -- prints: Admin
-- Read it as: if isAdmin is truthy, the result is "Admin", otherwise "User".
-- (One catch: it breaks if 'a' could itself be false or nil — rare for beginners.)The Truthiness Trap
In Lua, the only falsy values are nil and false. Everything else is truthy — including the number 0, the empty string "", and an empty table {}. If you come from JavaScript, Python, or C, this is backwards from what you expect, where 0 and "" are falsy.
if 0 then print("0 is TRUTHY in Lua!") end -- prints: 0 is TRUTHY in Lua!
if "" then print("empty string is truthy") end -- prints: empty string is truthy
if not nil then print("nil is falsy") end -- prints: nil is falsy
if not false then print("false is falsy") end -- prints: false is falsySo to check for zero you must be explicit — write if count == 0 then, never rely on 0 being falsy.
Your Turn: Temperature Check
The program below is almost complete — fill in the three blanks marked ___ using the hints in the comments, then run it and check your output matches.
-- 🎯 YOUR TURN — replace each ___ then run the code.
local temperature = 30
-- 1) If temperature is GREATER THAN OR EQUAL TO 30, print "Hot"
if temperature ___ 30 then -- 👉 the operator for "greater than or equal to"
print("Hot")
-- 2) Otherwise, if it is BELOW 10, print "Cold"
elseif temperature ___ 10 then -- 👉 the operator for "less than"
print("Cold")
-- 3) Otherwise print "Mild"
___ -- 👉 the keyword that catches everything else
print("Mild")
end
-- ✅ Expected output:
-- Hot4. Repeating Work with Loops
A loop runs a block of code over and over. Lua gives you three forms. The numeric for counts: for i = start, stop, step do (the step is optional and defaults to 1). The while loop repeats while a condition stays true, checking it at the top. The repeat...until loop runs the body first and tests at the bottom, so it always runs at least once. Note Lua has no ++ or -=, so you update counters in full like fuel = fuel - 1.
-- 1) NUMERIC for: for var = start, stop[, step] do ... end
for i = 1, 5 do
print(i) -- prints: 1, then 2, 3, 4, 5 (one per line)
end
-- A 3rd number is the STEP. Use a negative step to count down:
for i = 10, 1, -2 do
print(i) -- prints: 10, 8, 6, 4, 2
end
-- 2) while: repeat the body WHILE a condition stays true.
-- Lua has no -- or -= shortcuts, so write 'fuel = fuel - 1' in full.
local fuel = 3
while fuel > 0 do
print("Fuel:", fuel) -- prints: Fuel: 3 / Fuel: 2 / Fuel: 1
fuel = fuel - 1
end
-- 3) repeat ... until: like do-while — the body always runs AT LEAST once,
-- then the test is checked at the BOTTOM (loop ends when it becomes true).
local attempts = 0
repeat
attempts = attempts + 1
until attempts >= 3
print("Tried", attempts, "times") -- prints: Tried 3 times5. Looping Over Tables: pairs & ipairs
The generic for walks over a table. Use ipairs(t) for list-style tables — it visits integer keys 1, 2, 3... in order and stops at the first gap. Use pairs(t) to visit every key, including string keys like "name", though the order is not guaranteed. Exit any loop early with break. There is no continue in Lua — to skip an item, invert the test and wrap the rest of the body in an if.
-- The generic for walks over a table using an iterator function.
-- ipairs(t): array-style, integer keys 1,2,3... IN ORDER, stops at first gap.
local inventory = {"sword", "shield", "potion"}
for slot, item in ipairs(inventory) do
print(slot, item) -- prints: 1 sword / 2 shield / 3 potion
end
-- pairs(t): walks EVERY key, including string keys. Order is NOT guaranteed.
local player = { name = "Aria", level = 7, hp = 100 }
for key, value in pairs(player) do
print(key, value) -- prints each pair, e.g. name Aria / level 7 / hp 100
end
-- 'break' exits the nearest loop immediately:
for i = 1, 100 do
if i > 3 then break end -- stops the loop once i passes 3
print(i) -- prints: 1, 2, 3
end
-- Lua has NO 'continue'. To skip an iteration, wrap the rest in an if:
for i = 1, 5 do
if i % 2 == 0 then -- only act on ODD numbers
print(i, "is odd") -- prints: 1 is odd / 3 is odd / 5 is odd
end
endYour Turn: Count & Find
Two small gaps here: complete the numeric for's stop value, then add the keyword that exits a loop the moment a match is found. Fill in the blanks and run it.
-- 🎯 YOUR TURN — replace each ___ then run the code.
-- 1) Count from 1 up to 5 with a numeric for loop
for i = 1, ___ do -- 👉 the number to stop at
print(i)
end
-- 2) Find the first number over 100 in this list, then stop early
local readings = {40, 75, 130, 60}
for index, value in ipairs(readings) do
if value > 100 then
print("First over 100:", value)
___ -- 👉 the keyword that exits a loop immediately
end
end
-- ✅ Expected output:
-- 1
-- 2
-- 3
-- 4
-- 5
-- First over 100: 130Pro Tips
- 💡 Pair the operators: equal is
==, not-equal is~=. If you type!=you'll get a syntax error. - 💡 Guard against infinite loops: a
whileneeds something inside that eventually makes its condition false (e.g.fuel = fuel - 1), or it never stops. - 💡 Use the right walker:
ipairsfor ordered lists,pairsfor tables with named keys. - 💡 No continue? Flip the condition:
if value ~= 0 then ... endskips the zeros for you.
Common Errors (and the fix)
- "unexpected symbol near '='" — you wrote
!=for not-equal. Lua uses~=instead (e.g.if x ~= 0 then). - "'end' expected (to close 'if' ...) near <eof>" — you forgot the closing
end. Everyif,for,while, andfunctionneeds its ownend. - A test on
0or""behaving "wrong": remember onlynilandfalseare falsy —0and""are truthy. Compare explicitly with== 0. - "'<name>' expected near 'continue'" — Lua has no
continue. Wrap the rest of the loop body in anifinstead, or usegotoin Lua 5.2+. - Program hangs / never finishes: an infinite
while. Make sure the body changes the variable in the condition so it can become false.
📋 Quick Reference
| Concept | Syntax |
|---|---|
| if / elseif / else | if x then ... elseif y then ... else ... end |
| Equal / not equal | == ~= (not !=) |
| Logical | and or not |
| Ternary idiom | cond and a or b |
| Numeric for | for i = 1, 10, 2 do ... end |
| while | while cond do ... end |
| repeat | repeat ... until cond |
| Walk a list | for k, v in ipairs(t) do ... end |
| Walk all keys | for k, v in pairs(t) do ... end |
| Exit a loop | break (no continue) |
Frequently Asked Questions
Q: Why is "not equal" written as ~= instead of != in Lua?
It is simply Lua's chosen syntax: the inequality operator is ~= (a tilde followed by an equals sign). Writing != gives a syntax error such as "unexpected symbol near '='". Most C-style languages use !=, so this is the single most common mistake people make when arriving from JavaScript, C, or Python. Equality is == and inequality is ~= — memorise the pair together.
Q: Why does 'if 0 then' run its body in Lua?
Because in Lua the ONLY falsy values are nil and false. Everything else is truthy — including the number 0, the empty string "", and empty tables {}. This is different from JavaScript, Python, and C, where 0 and "" are falsy. If you want to test for zero you must say it explicitly, e.g. 'if count == 0 then', rather than relying on 0 being falsy.
Q: How do I 'continue' to the next loop iteration?
Lua has no continue keyword. The usual fix is to invert the test and wrap the remaining body in an if, so the code you wanted to skip simply does not run. For example, instead of skipping even numbers with continue, write 'if value % 2 ~= 0 then ... end' to act only on odd ones. From Lua 5.2 onwards you can also use goto with a ::continue:: label at the end of the loop body, but the if approach is clearer for beginners.
Q: When should I use while versus repeat...until?
Use while when the condition should be checked BEFORE the first run — the body might execute zero times. Use repeat...until when the body must run AT LEAST once and you only know whether to stop after trying (for example, reading input until the user types "quit"). A second difference: repeat checks its condition at the bottom, and any local variables declared inside the loop are still visible in that until test.
Q: What is the difference between pairs() and ipairs()?
ipairs() walks a table as an array: it visits integer keys 1, 2, 3... in order and stops at the first missing index (the first nil). pairs() visits EVERY key in the table, including string keys like "name", but in no guaranteed order. Use ipairs for ordered lists and pairs when you need to see all entries of a table that uses named keys.
Q: Why doesn't fuel-- or fuel -= 1 work in my loop?
Lua does not have the increment/decrement operators (++/--) or the compound assignment operators (+=, -=, *=, /=) found in C and JavaScript. You write the update out in full: fuel = fuel - 1, or count = count + 1. It is slightly more typing but keeps Lua's grammar small and unambiguous.
Mini-Challenge: FizzBuzz
No blanks this time — just a brief and an outline to keep you on track. FizzBuzz combines a loop, modulo, and the full if/elseif/else ladder, so it's the perfect end-of-lesson test. Build it, run it, and check your output against the example in the comments.
-- 🎯 MINI-CHALLENGE: FizzBuzz (the classic interview warm-up)
-- 1. Loop i from 1 to 15 with a numeric for.
-- 2. If i is divisible by BOTH 3 and 5, print "FizzBuzz".
-- (use the modulo operator: i % 3 == 0 is true when 3 divides i)
-- 3. Else if divisible by 3, print "Fizz".
-- 4. Else if divisible by 5, print "Buzz".
-- 5. Otherwise, print the number i itself.
--
-- ✅ Expected output (first lines):
-- 1
-- 2
-- Fizz
-- 4
-- Buzz
-- Fizz
-- ...
-- 14
-- FizzBuzz
-- your code here🎉 Lesson Complete!
- ✅
if/elseif/elsebranch your code — and every one closes withend - ✅ Compare with
==and~=(not!=), plus<><=>= - ✅ Combine conditions with the words
and/or/not; fake a ternary withcond and a or b - ✅ Only
nilandfalseare falsy —0and""are truthy - ✅ Loop with the numeric
for,while, andrepeat...until(runs at least once) - ✅ Walk tables with
ipairs(ordered) andpairs(all keys); exit early withbreak— Lua has nocontinue - ✅ Next lesson: Metatables and Metamethods — Lua's most powerful feature for OOP and operator overloading
Sign up for free to track which lessons you've completed and get learning reminders.