Lua - Loops

Overview

Estimated time: 30–35 minutes

Loops allow you to repeat code blocks multiple times. Lua provides three types of loops: for loops (numeric and generic), while loops, and repeat-until loops. This tutorial covers all loop types with practical examples.

Learning Objectives

  • Master numeric for loops for counting iterations
  • Use generic for loops with pairs and ipairs
  • Implement while loops for condition-based repetition
  • Apply repeat-until loops for post-test conditions
  • Understand loop control with break and continue patterns

Prerequisites

  • Understanding of Lua operators and conditions
  • Knowledge of tables and basic data types

Numeric For Loops

Numeric for loops iterate over a range of numbers:

Basic Syntax

-- for variable = start, stop, step do
--     -- loop body
-- end

-- Simple counting loop
for i = 1, 5 do
    print("Iteration", i)
end

print("---")

-- Counting backwards
for i = 5, 1, -1 do
    print("Countdown", i)
end

print("---")

-- Custom step size
for i = 0, 10, 2 do
    print("Even number:", i)
end

Expected Output:

Iteration	1
Iteration	2
Iteration	3
Iteration	4
Iteration	5
---
Countdown	5
Countdown	4
Countdown	3
Countdown	2
Countdown	1
---
Even number:	0
Even number:	2
Even number:	4
Even number:	6
Even number:	8
Even number:	10

Practical Numeric For Loop Examples

-- Calculate factorial
local function factorial(n)
    local result = 1
    for i = 1, n do
        result = result * i
    end
    return result
end

print("5! =", factorial(5))

-- Generate multiplication table
local function multiplication_table(num, max)
    print("Multiplication table for", num)
    for i = 1, max do
        print(num .. " x " .. i .. " = " .. (num * i))
    end
end

multiplication_table(7, 5)

-- Sum of numbers in range
local function sum_range(start, stop)
    local total = 0
    for i = start, stop do
        total = total + i
    end
    return total
end

print("Sum of 1 to 10:", sum_range(1, 10))

Expected Output:

5! =	120
Multiplication table for	7
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
Sum of 1 to 10:	55

Generic For Loops

Generic for loops iterate over collections using iterator functions:

Using ipairs() for Arrays

local fruits = {"apple", "banana", "orange", "grape"}

-- ipairs() for array-like tables (consecutive integer keys)
print("Fruits list:")
for index, fruit in ipairs(fruits) do
    print(index .. ".", fruit)
end

-- Processing array elements
local numbers = {10, 20, 30, 40, 50}
local sum = 0

for i, num in ipairs(numbers) do
    sum = sum + num
    print("Adding", num, "- Running total:", sum)
end

print("Final sum:", sum)

Expected Output:

Fruits list:
1.	apple
2.	banana
3.	orange
4.	grape
Adding	10	- Running total:	10
Adding	20	- Running total:	30
Adding	30	- Running total:	60
Adding	40	- Running total:	100
Adding	50	- Running total:	150
Final sum:	150

Using pairs() for All Table Elements

local student = {
    name = "Alice",
    age = 20,
    grade = "A",
    subjects = {"Math", "Physics", "Chemistry"}
}

-- pairs() for all key-value pairs
print("Student information:")
for key, value in pairs(student) do
    if type(value) == "table" then
        print(key .. ":", table.concat(value, ", "))
    else
        print(key .. ":", value)
    end
end

-- Counting table elements
local inventory = {
    sword = 1,
    shield = 2,
    potion = 5,
    gold = 100
}

local item_count = 0
local total_value = 0

for item, quantity in pairs(inventory) do
    item_count = item_count + 1
    if item == "gold" then
        total_value = total_value + quantity
    else
        total_value = total_value + quantity * 10  -- Assume each item worth 10 gold
    end
    print("Item:", item, "Quantity:", quantity)
end

print("Total items:", item_count)
print("Total value:", total_value)

Expected Output:

Student information:
subjects:	Math, Physics, Chemistry
name:	Alice
age:	20
grade:	A
Item:	sword	Quantity:	1
Item:	shield	Quantity:	2
Item:	potion	Quantity:	5
Item:	gold	Quantity:	100
Total items:	4
Total value:	180

While Loops

While loops continue as long as a condition is true:

Basic While Loop

-- Simple counting with while
local count = 1
while count <= 5 do
    print("Count:", count)
    count = count + 1
end

-- User input simulation (in practice, you'd use io.read())
local attempts = 0
local max_attempts = 3
local success = false

while attempts < max_attempts and not success do
    attempts = attempts + 1
    print("Attempt", attempts)
    
    -- Simulate random success (normally this would be user input)
    if attempts == 2 then  -- Simulate success on second attempt
        success = true
        print("Success!")
    else
        print("Failed, trying again...")
    end
end

if not success then
    print("Max attempts reached")
end

Expected Output:

Count:	1
Count:	2
Count:	3
Count:	4
Count:	5
Attempt	1
Failed, trying again...
Attempt	2
Success!

Practical While Loop Examples

-- Number guessing game logic
local function guess_number_game()
    local secret = 42  -- In real game, this would be random
    local guess = 0
    local attempts = 0
    
    print("Guess the number between 1 and 100!")
    
    while guess ~= secret and attempts < 5 do
        attempts = attempts + 1
        
        -- Simulate user guesses
        local guesses = {25, 60, 40, 42}
        guess = guesses[attempts] or 0
        
        print("Attempt " .. attempts .. ": Guessing " .. guess)
        
        if guess < secret then
            print("Too low!")
        elseif guess > secret then
            print("Too high!")
        else
            print("Correct! You won in " .. attempts .. " attempts!")
        end
    end
    
    if guess ~= secret then
        print("Game over! The number was " .. secret)
    end
end

guess_number_game()

-- Processing until condition is met
local function process_queue()
    local queue = {1, 2, 3, 4, 5}
    local processed = 0
    
    while #queue > 0 do
        local item = table.remove(queue, 1)  -- Remove first item
        processed = processed + 1
        print("Processing item " .. item .. " (Queue size: " .. #queue .. ")")
        
        -- Stop if we've processed 3 items
        if processed >= 3 then
            print("Processed enough items, stopping")
            break
        end
    end
    
    print("Remaining in queue:", #queue)
end

process_queue()

Expected Output:

Guess the number between 1 and 100!
Attempt 1: Guessing 25
Too low!
Attempt 2: Guessing 60
Too high!
Attempt 3: Guessing 40
Too low!
Attempt 4: Guessing 42
Correct! You won in 4 attempts!
Processing item 1 (Queue size: 4)
Processing item 2 (Queue size: 3)
Processing item 3 (Queue size: 2)
Processed enough items, stopping
Remaining in queue:	2

Repeat-Until Loops

Repeat-until loops execute at least once and continue until a condition becomes true:

-- Basic repeat-until
local x = 1
repeat
    print("x =", x)
    x = x + 1
until x > 3

print("---")

-- Menu system example
local function show_menu()
    local choice
    local valid_choices = {1, 2, 3, 4}
    
    repeat
        print("\n=== Menu ===")
        print("1. View Profile")
        print("2. Settings")
        print("3. Help")
        print("4. Exit")
        
        -- Simulate user choice (in real program, use io.read())
        choice = math.random(1, 5)  -- Sometimes invalid choice
        print("User chose:", choice)
        
        if choice == 1 then
            print("Showing profile...")
        elseif choice == 2 then
            print("Opening settings...")
        elseif choice == 3 then
            print("Displaying help...")
        elseif choice == 4 then
            print("Goodbye!")
        else
            print("Invalid choice, please try again")
            choice = nil  -- Force loop to continue
        end
        
    until choice == 4  -- Continue until user chooses exit
end

-- Simulate one iteration of the menu
math.randomseed(42)  -- For consistent output
local choice
repeat
    choice = math.random(1, 5)
    print("Simulated choice:", choice)
    if choice == 4 then
        print("Exit chosen")
    elseif choice > 4 then
        print("Invalid choice")
    else
        print("Valid menu choice")
    end
until choice == 4 or choice <= 3  -- Exit on valid choice or exit

Expected Output:

x =	1
x =	2
x =	3
---
Simulated choice:	2
Valid menu choice

Nested Loops

Loops can be nested inside other loops:

-- Multiplication table (nested for loops)
print("Multiplication Table (1-5):")
print("   ", end="")
for j = 1, 5 do
    io.write(string.format("%4d", j))
end
print()

for i = 1, 5 do
    io.write(string.format("%3d:", i))
    for j = 1, 5 do
        io.write(string.format("%4d", i * j))
    end
    print()
end

-- Matrix operations
local function create_matrix(rows, cols, value)
    local matrix = {}
    for i = 1, rows do
        matrix[i] = {}
        for j = 1, cols do
            matrix[i][j] = value or 0
        end
    end
    return matrix
end

local function print_matrix(matrix)
    for i = 1, #matrix do
        local row = ""
        for j = 1, #matrix[i] do
            row = row .. string.format("%3d ", matrix[i][j])
        end
        print(row)
    end
end

-- Create and display a 3x3 matrix
local matrix = create_matrix(3, 3, 0)

-- Fill matrix with values
for i = 1, 3 do
    for j = 1, 3 do
        matrix[i][j] = i + j
    end
end

print("\n3x3 Matrix:")
print_matrix(matrix)

Expected Output:

Multiplication Table (1-5):
       1   2   3   4   5
  1:   1   2   3   4   5
  2:   2   4   6   8  10
  3:   3   6   9  12  15
  4:   4   8  12  16  20
  5:   5  10  15  20  25

3x3 Matrix:
  2   3   4 
  3   4   5 
  4   5   6 

Loop Control

Use break to exit loops early:

-- Finding first element that meets condition
local numbers = {3, 7, 2, 9, 1, 8, 5}
local target = 9

print("Looking for", target)
for i, num in ipairs(numbers) do
    print("Checking position " .. i .. ": " .. num)
    if num == target then
        print("Found " .. target .. " at position " .. i)
        break  -- Exit the loop early
    end
end

-- Early exit from while loop
local function find_first_prime()
    local number = 2
    
    while true do  -- Infinite loop
        local is_prime = true
        
        -- Check if number is prime
        for i = 2, math.sqrt(number) do
            if number % i == 0 then
                is_prime = false
                break  -- Exit inner loop
            end
        end
        
        if is_prime then
            print("First prime found:", number)
            break  -- Exit outer loop
        end
        
        number = number + 1
    end
end

find_first_prime()

-- Continue pattern (Lua doesn't have continue, but we can simulate it)
print("Even numbers from 1 to 10:")
for i = 1, 10 do
    if i % 2 ~= 0 then
        -- Skip odd numbers (simulate continue)
        goto continue
    end
    
    print("Even number:", i)
    
    ::continue::  -- Label for goto
end

Expected Output:

Looking for	9
Checking position 1: 3
Checking position 2: 7
Checking position 3: 2
Checking position 4: 9
Found 9 at position 4
First prime found:	2
Even numbers from 1 to 10:
Even number:	2
Even number:	4
Even number:	6
Even number:	8
Even number:	10

Advanced Loop Patterns

Iterator Pattern

-- Custom iterator for even numbers
local function even_numbers(max)
    local i = 0
    return function()
        i = i + 2
        if i <= max then
            return i
        end
    end
end

print("Even numbers up to 10:")
for num in even_numbers(10) do
    print(num)
end

-- Iterator for file-like data
local function lines_iterator(text)
    local lines = {}
    for line in string.gmatch(text, "([^\n]+)") do
        table.insert(lines, line)
    end
    
    local i = 0
    return function()
        i = i + 1
        return lines[i]
    end
end

local sample_text = "Line 1\nLine 2\nLine 3"
print("Processing lines:")
for line in lines_iterator(sample_text) do
    print("Processing:", line)
end

Common Pitfalls

  • Infinite loops: Ensure loop conditions will eventually become false
  • Off-by-one errors: Check your loop bounds carefully
  • Modifying tables during iteration: Can cause unexpected behavior
  • Using wrong iterator: Use ipairs() for arrays, pairs() for all table elements
  • Forgetting do: All loop statements require do

Checks for Understanding

  1. What are the three types of loops in Lua?
  2. What's the difference between pairs() and ipairs()?
  3. How do you create a numeric for loop that counts backwards?
  4. What's the difference between while and repeat-until loops?
  5. How do you exit a loop early?
Show answers
  1. Numeric for loops, generic for loops, while loops, and repeat-until loops (4 types total)
  2. ipairs() iterates over consecutive integer keys starting from 1; pairs() iterates over all key-value pairs
  3. Use a negative step: for i = 10, 1, -1 do
  4. While loops test the condition before each iteration; repeat-until loops test after, so they always execute at least once
  5. Use the break statement

Next Steps

Now that you can repeat code with loops, you're ready to learn about functions, which allow you to organize and reuse code blocks effectively.