Numbers, Triangles, and Tables: Mastering Loops

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, reusable

When 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 result

Think 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 stop in 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 result

Think about:

  • How does the step parameter change the iteration?
  • When do you calculate the square—before or after the loop check?
  • Could you use num ** 2 instead of num * 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 result

Think about:

  • Why does the range go from 1 to n + 1?
  • What’s the pattern between row number and number of asterisks?
  • Why add \n after each row?
  • How does get_row() make get_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 result

Think 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 works

3. 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 value

4. 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 = 12

If 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 test

Python:

1cd python
2pip install pytest  # If not already installed
3pytest

Reading 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:

  1. Clone the repository from https://github.com/ZCW-Summer25/NumbersTrianglesTables
  2. Choose your language (Java or Python) and navigate to that directory
  3. Start with NumberUtilities: Get comfortable with basic loops
  4. Move to TriangleUtilities: Practice nested loops and patterns
  5. Finish with TableUtilities: Master two-dimensional iteration
  6. Run tests frequently: Don’t wait until everything is done
  7. 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.

ℹ️
Repository Link Find the starter code and complete instructions at: https://github.com/ZCW-Summer25/NumbersTrianglesTables