In this article, I continue with my fundamentals series by describing control flow constructs in Lua. In Part 1, I explained variables and the values that go in them, along with a means to manipulate values using operators. Let’s use the variables and make decisions in our code with them.
Comparison Operators
I conveniently left out comparison operators from the previous article since it wouldn’t be immediately obvious what they’re used for. These operators compare two values and give back a boolean (true or false). They compare! You’re familiar with some of them from math class (greater than, less than and their equal-to variants):
-- Just some variables for the purpose of demonstration friends = 15 gold = 500 guesses = 3 name = "Ozzy" -- Comparison operators compare two values print(friends > 10) --> true. Greater than print(gold >= 250) --> true. Greater than or equal to print(guesses < 5) --> true. Less than print(guesses <= 3) --> true. Less than or equal to print(name == "Ozzy") --> true. Equality print(name ~= "Joe") --> true. Inequality -- The "not" operator flips a boolean: print(not true) --> false print(not false) --> true
Notice how equality is tested with double equals signs. The single equals sign is reserved for assignment. Inequality uses a tilde (~ ). All the other comparison operators are used with numbers. We’ll use these expressions in the print calls for the following constructs.
Comparison operators have a lower precedence than “and” and “or” boolean operators, which were covered in part 1. Use these to test two expressions at once without the need for parenthesis:
-- Scenario: testing if we can buy an item (have money and space in backpack) gold = 300 backpack_space = 5 cost = 250 size_of_item = 10 print(gold >= cost and backpack_space >= size_of_item) --> false; 5 < 10 -- Another scenario: testing if we've completed a quest princessIsSaved = true packageDelivered = false print(princessIsSaved and packageDelivered) --> false -- One more scenario: strength = 10 agility = 5 print(strength > 8 or agility > 8) --> true, both conditions hold!
If-Then
If-then checks an expression and runs a block of code once if that expression is non-false and non-nil (i.e. it holds).
First up is the if-then-else construct. It checks if the expression between if and then evaluates to a non-nil and non-false value (i.e. the condition holds). If it does the code after then is run. If not, it skips forward and runs the code between else and end (or past the block entirely if there’s no else). Here’s an elementary example that displays a message with a simple condition:
strength = 12 if strength > 10 then print("You are very strong!") --> Will print because 12 > 10 end
Adding an else block allows us to run another block of code if the condition didn’t hold.
Here’s an example of buying an item – we need enough gold, so we use the gold >= swordCost expression inside if-then to test this. If this condition holds, we subtract the gold and set the hasSword variable to true. Otherwise, print how much gold the player needs by subtracting.
gold = 300 hasSword = false swordCost = 250 if gold >= swordCost then -- Expression checks if we have enough gold gold = gold - 250 hasSword = true print("You bought the sword! You have " .. gold .. " gold left.") else print("You cannot afford the sword. You need " .. (swordCost - gold) .. " gold") end
Use any number of elseif blocks to check multiple conditions sequentially.
What if you need to check multiple cases? For that, you can use any number of elseif (one word). Consider this example of checking what key a player pressed:
keyPressed = "s" if keyPressed = "i" then print("Opening the inventory window...") elseif keyPressed == "s" then print("Opening the spells window...") elseif keyPressed == "c" then print("Opening the character window...") else -- else-block runs when if and elseifs conditions fail print("I don't know what you just pressed.") end
The if condition is checked first, followed by the elseif in order. If just one condition holds, it runs that block and jumps over the rest.
While-do
A while-do will check a condition and run some code much like if-then, but it also checks again repeatedly after each time the code is run.
Next up is while-do. This works just like a simple if-then-end works, but when the block is done running it loops back to the condition again and again until the condition is false. This block of code counts from 1 to 10 using a while-do:
counter = 1 while counter <= 10 do print(counter) --> 1, 2, 3...10 etc counter = counter + 1 -- Increment the counter end print("I'm done counting now")
Use break to jump out of the loop early.
You can also stop the loop early by using break. This is very useful if your stopping condition is somewhat complex or if your algorithm has special stopping conditions that would otherwise be messy to put between while-do. In this example, we’ll stop early when counter hits 7:
num = 1 while num <= 10 do print(num) num = num - 1 if num == 7 then break -- Jump out of the loop immediately end end print("I'm done counting now")
Repeat-Until
A repeat-until block works like while-do, except that it checks after running the block of code instead of before. It always runs at least once.
As the name implies, repeat-until defines a block of code and tests an expression at the end of the block. If the expression holds (like in if-then), the entire block is repeated once more. It continues to do so until the expression is false.
The below example uses repeat-until to display a progression of the Collatz Conjecture. In a nutshell: start with a number. If it is even, divide it by two. If it is odd, multiply by three and add 1. Continue until you reach 1. Is there a number that will never reach 1? Find out by running the code below:
-- Simulate a progression of the Collatz conjecture. -- https://en.wikipedia.org/wiki/Collatz_conjecture num = 9 -- Can you find a number that will never reach 1? repeat print(num) -- To test cardinality, use the modulo operator (%) -- to take the remainder of a division by 2 if num % 2 == 0 then -- num is even num = num / 2 else -- num is odd num = num * 3 + 1 end until num == 1 print(num) --> 1 print("Done")
There’s another control structure that’s a bit more complicated, called the for loop. There’s two variants: numeric for and generic for. I’m saving this for later since it is a bit more complicated in Lua than in other languages. For now, you can accomplish the same tasks using if-then, while-do, and repeat-until.
Conclusion
We now have a means of performing complex logic in Lua code now. We’re not yet Turing complete, but we’re getting close. You should experiment with these constructs to really understand how they work.
Exercises
- It’s a hot day, and staying hydrated is important. You have a cylindrical water bottle with a height and radius. You plan on going on an adventure which requires you to drink 100 units of water. Given the dimensions of some bottle, print “Hydrated” if the bottle will hold enough water for you to stay hydrated during the adventure. Otherwise, print “Thirsty”. Solution
- You have a little brother who is an annoying copycat. When you say a word, he’ll repeat it once for every letter in the word. Hint: Use string.len(s) to get the length of a string. Ex: string.len(“Hello”) will give 5. Given a word you’ve said, print what your brother would say. Solution
- Imagine a game where a player starts at level 1 and gains a level when they gain 10 times their current level in experience. Given a player’s total experience, calculate what level they are. Solution