Numbers, Triangles, and Tables: Mastering Loops
Here’s something most programming courses don’t tell you upfront: you’ll spend the rest of your career writing loops. For loops, while loops, iterator patterns—they’re fundamental to everything you’ll build. This lab isn’t about making something flashy. It’s about getting so comfortable with loops that you stop thinking about the syntax and start thinking about the patterns.
Why This Lab Matters
This feels like a simple assignment. Generate some numbers, print some asterisks, make a multiplication table. And yeah, on the surface, that’s what it is. But here’s what you’re actually learning:
Loop Mechanics: You need to understand exactly when a loop starts, when it ends, what happens in each iteration, and how variables change over time. Get this wrong and you’ll debug off-by-one errors for the rest of your life.
String Building: Real programs generate output constantly—reports, logs, formatted data, user interfaces. This lab teaches you how to build strings programmatically, which is something you’ll do daily in any development job.
Pattern Recognition: Triangles and tables aren’t random exercises. They’re patterns. Learning to see the pattern behind the visual output is the skill that lets you translate business requirements into code.
Return vs. Print: One of the most important lessons here is understanding the difference between returning a value and printing it. Functions that return values are composable, testable, and reusable. Functions that print directly are none of those things.
The Three Challenges
You’re building three utility modules. Each one focuses on a different aspect of loop mastery:
1. NumberUtilities: Counting and Generating
Generate sequences of numbers based on different rules:
- Even numbers in a range
- Odd numbers in a range
- Squared values with a step
- Custom ranges with step values
- Exponentiation patterns
What you’re learning: Basic loop iteration, conditional logic inside loops, and numeric operations.
2. TriangleUtilities: Pattern Building
Generate ASCII triangle patterns of different sizes:
- Single rows of asterisks
- Small triangles (4 rows)
- Large triangles (10 rows)
- Custom-sized triangles
What you’re learning: Nested loops, visual pattern generation, and building output incrementally.
3. TableUtilities: Formatted Output
Create multiplication tables with proper formatting:
- Small tables (4×4)
- Large tables (10×10)
- Custom-sized tables with alignment
What you’re learning: Nested loops with dual variables, string formatting, and producing structured output.
The Critical Concept: Return vs. Print
Let me emphasize this because it trips up every beginner:
Your functions must RETURN strings, not print them.
Here’s why this matters:
1# Bad: Prints directly
2def get_even_numbers_bad(start, stop):
3 for i in range(start, stop):
4 if i % 2 == 0:
5 print(i) # Can't test this, can't compose it
6
7# Good: Returns a string
8def get_even_numbers(start, stop):
9 result = ""
10 for i in range(start, stop):
11 if i % 2 == 0:
12 result += str(i)
13 return result # Testable, composable, reusableWhen you return values, your code becomes a building block. When you print directly, you’ve created a dead end.
Phase-by-Phase Implementation
Phase 1: Simple Number Sequences
Start with the easiest challenge: generating even and odd numbers.
1def get_even_numbers(start, stop):
2 """Generate even numbers from start to stop (exclusive)"""
3 result = ""
4 for num in range(start, stop):
5 if num % 2 == 0:
6 result += str(num)
7 return resultThink about:
- What does
range(start, stop)actually give you? - How do you check if a number is even? (Hint: modulo operator)
- Why do we need
str(num)before concatenating? - What happens if there are no even numbers in the range?
Test it:
1print(get_even_numbers(5, 20)) # Should output: "681012141618"
2print(get_even_numbers(10, 15)) # Should output: "1012"Common mistakes:
- Including
stopin the range (remember, range is exclusive at the end) - Forgetting to convert numbers to strings before concatenating
- Testing for odd when you meant even (or vice versa)
Phase 2: Loops with Steps and Operations
Now level up with squares and exponentiation:
1def get_square_numbers(start, stop, step):
2 """Generate squared values with a step"""
3 result = ""
4 for num in range(start, stop, step):
5 squared = num * num
6 result += str(squared)
7 return resultThink about:
- How does the
stepparameter change the iteration? - When do you calculate the square—before or after the loop check?
- Could you use
num ** 2instead ofnum * num? (Yes, and you should know both)
Test it:
1print(get_square_numbers(5, 15, 5)) # Should output: "25100"
2print(get_square_numbers(1, 5, 1)) # Should output: "1491625"Phase 3: Building Visual Patterns
Triangles are where loops get interesting. You need to think in two dimensions:
1def get_row(width):
2 """Generate a row of asterisks"""
3 return "*" * width
4
5def get_triangle(n):
6 """Generate an n-row triangle"""
7 result = ""
8 for i in range(1, n + 1):
9 result += get_row(i) + "\n"
10 return resultThink about:
- Why does the range go from 1 to
n + 1? - What’s the pattern between row number and number of asterisks?
- Why add
\nafter each row? - How does
get_row()makeget_triangle()simpler?
Test it:
1print(get_triangle(4))
2# Should output:
3# *
4# **
5# ***
6# ****This is decomposition in action. You break the problem into smaller pieces.
get_row() handles one row. get_triangle() handles multiple rows by calling
get_row() repeatedly. This is how you manage complexity.
Phase 4: Nested Loops and Formatting
Multiplication tables require nested loops—a loop inside a loop:
1def get_multiplication_table(width):
2 """Generate a multiplication table of given width"""
3 result = ""
4 for row in range(1, width + 1):
5 for col in range(1, width + 1):
6 product = row * col
7 result += f"{product:>3} |"
8 result += "\n"
9 return resultThink about:
- Why do you need TWO loops?
- What does the outer loop represent? The inner loop?
- What’s happening with
{product:>3}? (Right-align in 3 characters) - Why add the newline after the inner loop finishes?
Test it:
1print(get_multiplication_table(3))
2# Should output:
3# 1 | 2 | 3 |
4# 2 | 4 | 6 |
5# 3 | 6 | 9 |The nested loop pattern:
- Outer loop: controls rows
- Inner loop: controls columns
- After inner loop completes: move to next row
This pattern shows up everywhere: processing 2D arrays, generating grids, working with matrices, building tables in web apps.
String Building Strategies
You have multiple ways to build strings in Python. Know them all:
Concatenation (Simple but works)
1result = ""
2for i in range(5):
3 result += str(i)Pros: Easy to understand, works everywhere
Cons: Can be slow for very large strings (creates new string each time)
Join (More Pythonic)
1numbers = [str(i) for i in range(5)]
2result = "".join(numbers)Pros: Faster for large strings, more “Pythonic”
Cons: Requires building a list first
F-strings (Modern and readable)
1result = ""
2for i in range(5):
3 result += f"{i}"Pros: Clean syntax, supports formatting
Cons: Still concatenating under the hood
For this lab, any approach works. Choose what makes sense to you. The important thing is understanding what you’re doing, not memorizing “the right way.”
Common Pitfalls and How to Avoid Them
1. Off-by-One Errors
The classic loop mistake. Remember: range(start, stop) goes UP TO but NOT
INCLUDING stop.
1# Wrong: Misses the last number you want
2for i in range(1, 10): # Goes 1-9, not 1-10
3 print(i)
4
5# Right: Include one extra
6for i in range(1, 11): # Goes 1-10
7 print(i)2. Forgetting to Convert to String
You can’t concatenate integers to strings directly:
1# Wrong: TypeError
2result = ""
3result += 5 # Can't do this!
4
5# Right: Convert first
6result = ""
7result += str(5) # This works3. Printing Instead of Returning
Your tests will fail if you print instead of return:
1# Wrong: Tests can't capture this
2def get_even_numbers(start, stop):
3 for i in range(start, stop):
4 if i % 2 == 0:
5 print(i) # Goes to console, not to caller
6
7# Right: Tests can verify this
8def get_even_numbers(start, stop):
9 result = ""
10 for i in range(start, stop):
11 if i % 2 == 0:
12 result += str(i)
13 return result # Caller receives the value4. Not Understanding Nested Loops
Each iteration of the outer loop runs the ENTIRE inner loop:
1for row in range(3): # Runs 3 times
2 for col in range(4): # Runs 4 times PER row
3 print(row, col)
4# Total iterations: 3 × 4 = 12If you’re confused about what’s happening, add print statements to see the iteration count:
1iteration = 0
2for row in range(3):
3 for col in range(4):
4 iteration += 1
5 print(f"Iteration {iteration}: row={row}, col={col}")Testing Your Code
The repository comes with tests. Here’s how to use them effectively:
Java:
1cd java
2mvn testPython:
1cd python
2pip install pytest # If not already installed
3pytestReading test failures:
When a test fails, it tells you:
- What function was being tested
- What input was provided
- What output was expected
- What output you actually returned
Use this information to debug. Don’t just stare at your code—look at what the test expected and compare it to what you returned.
Testing incrementally:
Don’t write all three utilities and then test. Write one function, test it, make it pass, then move to the next. This is how professionals work.
The Bigger Picture
These exercises might seem basic, but they’re teaching you patterns you’ll use constantly:
Number generation → Database queries returning ranges, pagination, batch processing
Triangle patterns → Understanding nested structures, building UI components, generating reports
Multiplication tables → Formatting data for display, building grids, creating structured output
Six months from now, you won’t be making multiplication tables. But you WILL be using nested loops to process data, format output, and generate structured information. The patterns are the same.
Going Further (Optional Extensions)
Once you’ve got the basics working, try these challenges:
Advanced Number Utilities
- Generate prime numbers in a range
- Create Fibonacci sequences
- Generate numbers in different bases (binary, hexadecimal)
Pattern Variations
- Right-aligned triangles
- Inverted triangles
- Diamond patterns
- Hollow triangles (asterisks only on edges)
Table Enhancements
- Addition tables, not just multiplication
- Custom operators (subtraction, division)
- Formatted decimal alignment
- Color-coded output (for terminal apps)
The Professional Mindset
Here’s what this lab is really teaching:
Precision: Loops require exact thinking. Off by one? Wrong output. Wrong condition? Infinite loop. This precision is what separates working code from broken code.
Decomposition: Breaking problems into smaller functions. get_row() helps
build get_triangle(). This is how you manage complexity in real systems.
Testability: Returning values instead of printing makes your code testable. Tests are how professional teams ensure code quality.
Pattern Recognition: Seeing the loop pattern behind the visual output. This skill translates directly to translating business requirements into code.
Getting Started
Here’s your action plan:
- Clone the repository from https://github.com/ZCW-Summer25/NumbersTrianglesTables
- Choose your language (Java or Python) and navigate to that directory
- Start with NumberUtilities: Get comfortable with basic loops
- Move to TriangleUtilities: Practice nested loops and patterns
- Finish with TableUtilities: Master two-dimensional iteration
- Run tests frequently: Don’t wait until everything is done
- Experiment: Once tests pass, try the optional extensions
Remember: Loops Are Forever
You will write loops in every program you create for the rest of your career. Getting comfortable with them now—understanding how they work, what patterns they follow, and how to debug them—is time incredibly well spent.
Don’t rush through this. Take the time to understand each loop’s behavior. Print intermediate values. Draw diagrams of what’s happening. The investment you make now in truly understanding loops will pay off every single day you write code.