Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1-Based Indexing

The Zero-Based Confusion

Quick question: What’s the first item in this list?

Shopping List:
1. Milk
2. Eggs
3. Bread

Everyone says “Milk” - it’s item #1.

Now, in most programming languages:

# Python, JavaScript, Java, C++, etc.
shopping = ["Milk", "Eggs", "Bread"]
first = shopping[0]  # 0? But we just said it's #1!

Why this disconnect?

Historical Reasons

Zero-based indexing comes from C, where arrays are implemented as pointers:

// C language
int array[5];
// array[0] means: "address of array + (0 * sizeof(int))"
// array[3] means: "address of array + (3 * sizeof(int))"

This was a hardware optimization from the 1970s. The first element is offset by zero bytes, so accessing it is slightly faster.

But that’s an implementation detail! Why should humans care about memory offsets?

The SFX Solution

SFX uses 1-based indexing because that’s how humans count:

Story:
    Shopping is ["Milk", "Eggs", "Bread"]

    First is Shopping[1]   # Milk ✓
    Second is Shopping[2]  # Eggs ✓
    Third is Shopping[3]   # Bread ✓

    # The length matches the last index!
    Count is Shopping.Length  # 3
    Last is Shopping[Count]   # Bread ✓

Why 1-Based is Better

1. Matches Human Counting

Story:
    # Task: Print the first 3 items
    Items is ["A", "B", "C", "D", "E"]

    Repeat 3 times with I:
        Print Items[I]  # I goes 1, 2, 3 - perfect!

Compare to zero-based:

# Confusing: Print items 0, 1, 2 to get the "first 3"
for i in range(3):  # range(3) means 0,1,2 - huh?
    print(items[i])

2. Length Matches Last Index

Story:
    Numbers is [10, 20, 30, 40, 50]
    Count is Numbers.Length  # 5
    Last is Numbers[Count]   # 50 ✓ Makes sense!

Zero-based:

numbers = [10, 20, 30, 40, 50]
count = len(numbers)  # 5
last = numbers[count]  # ERROR! Out of bounds!
last = numbers[count - 1]  # 50 (why -1?)

3. Fewer Off-By-One Errors

The infamous “off-by-one error” is often caused by zero-based thinking:

# Bug-prone (0-based)
for i in range(len(items)):  # 0 to length-1
    # Easy to forget the -1 in range(start, end-1)

# Or:
for i in range(1, len(items)):  # Accidentally skips first item!

SFX:

# Clear (1-based)
Repeat Items.Length times with I:
    Print Items[I]  # I goes 1, 2, 3... naturally

4. Clearer Slicing

Story:
    Numbers is [10, 20, 30, 40, 50]

    # Get items 2 through 4
    Slice is Numbers.Slice(2, 4)  # [20, 30, 40] ✓
    # Start at 2, end at 4, includes both!

Compare to zero-based (Python):

numbers = [10, 20, 30, 40, 50]
slice = numbers[1:4]  # [20, 30, 40]
# Start at 1 (second item), end BEFORE 4 - confusing!

Common Scenarios

Looping with Index

Story:
    Names is ["Alice", "Bob", "Charlie"]

    # Natural counting
    Repeat Names.Length times with I:
        Print "Person " + I + ": " + Names[I]
    # Output:
    # Person 1: Alice
    # Person 2: Bob
    # Person 3: Charlie

Finding an Index

Story:
    Items is ["apple", "banana", "cherry"]
    Target is "banana"

    Found is 0
    Repeat Items.Length times with I:
        If Items[I] = Target:
            Found is I
            Break

    If Found > 0:
        Print "Found at position " + Found  # Position 2 ✓

Accessing First and Last

Story:
    Numbers is [5, 10, 15, 20, 25]

    First is Numbers[1]             # 5
    Last is Numbers[Numbers.Length] # 25

    # Middle element (for odd-length lists)
    Middle is Numbers[(Numbers.Length + 1) / 2]

Frequently Asked Questions

“But zero-based is more efficient!”

No. Modern compilers optimize both equally. The difference is nanoseconds.

SFX’s JIT compiler generates the same machine code whether you write array[1] or array[0] - it just adjusts the offset.

“Zero-based is standard everywhere!”

So were punch cards, GOTO statements, and null pointers. Just because something is common doesn’t make it right.

Many successful languages use 1-based indexing:

  • MATLAB (engineering/scientific computing)
  • R (statistics)
  • Lua (game scripting)
  • Fortran (still used in supercomputing)

“What about C interop?”

SFX’s stdlib handles the conversion. When you call a C library, SFX adjusts indices automatically.

# You write (1-based)
Result is SomeArray[1]

# SFX internally (0-based for C interop)
# result = some_array[0]

“I’ll get confused switching between languages!”

People switch between:

  • Languages that start weeks on Sunday vs Monday
  • Countries that drive on left vs right
  • Date formats (MM/DD/YY vs DD/MM/YY)

Programmers are smart. You can handle lists starting at 1 in SFX and 0 in Python.

Examples

Ranking

Concept: Leaderboard
    Scores

    To ShowRankings:
        # Sort in descending order
        Sorted is This.Scores.Sort("desc")

        Print "=== Rankings ==="
        Repeat Sorted.Length times with Position:
            Player is Sorted[Position]
            Print "#" + Position + ": " + Player["name"] + " - " + Player["score"]

Story:
    Create Leaderboard Called Board
    Set Board.Scores to [
        { name: "Alice", score: 95 },
        { name: "Bob", score: 87 },
        { name: "Charlie", score: 92 }
    ]

    Board.ShowRankings
    # Output:
    # === Rankings ===
    # #1: Alice - 95
    # #2: Charlie - 92
    # #3: Bob - 87

Notice how the rank matches the index naturally!

Pagination

Story:
    AllItems is [/* 100 items */]
    PageSize is 10
    CurrentPage is 1

    # Page 1: items 1-10
    # Page 2: items 11-20
    # etc.
    StartIndex is ((CurrentPage - 1) * PageSize) + 1
    EndIndex is CurrentPage * PageSize

    # Get items for current page
    PageItems is AllItems.Slice(StartIndex, EndIndex)

Grid/Matrix

Story:
    # 3x3 grid
    Grid is [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ]

    # Access row 2, column 3
    Value is Grid[2][3]  # 6 (second row, third item)

    # Human-readable coordinates
    Print "Row 1, Col 1: " + Grid[1][1]  # 1 ✓

Migration from 0-Based Languages

If you’re coming from Python/JavaScript/Java:

# Python (0-based)
first = items[0]
second = items[1]
last = items[len(items) - 1]
# SFX (1-based)
First is Items[1]
Second is Items[2]
Last is Items[Items.Length]

Tip: Think “first, second, third” not “zeroth, first, second.”

Best Practices

Loop from 1 to Length

# Good
Repeat List.Length times with I:
    Print List[I]

Use For Each When You Don’t Need Index

# Even better when index isn't needed
For each Item in List:
    Print Item

Check Bounds

# Safe access
Index is 5
If Index >= 1 and Index <= List.Length:
    Value is List[Index]
Else:
    Print "Index out of range"

Summary

SFX uses 1-based indexing because:

Natural - Matches how humans count ✓ Clearer - First item is index 1, not 0 ✓ Consistent - Length equals last index ✓ Fewer errors - Less off-by-one confusion ✓ No performance cost - JIT optimizes equally

Programming is for humans, not computers. Let’s count like humans.


Next: No Null Pointers - Safe defaults and the Option type