Swift: نطاقات وتسلسلات Swift: Range وClosedRange وStride

A range is like a segment on a number line — consecutive integers from A to B. This lesson covers how to create, use, and customize all range and sequence types in Swift.

1. What You'll Learn


2. An Event Planner's Real Story

(1) Pain: Manually generating 200 seat numbers — Excel froze

Charlie is organizing a 200-person tech conference. He needs to generate seat numbers: rows A through T (20 rows), 10 seats per row, assign attendees to seats, and produce a seating chart. He started by dragging the fill handle in Excel, but every time he added a VIP seat he had to readjust the entire sheet:

SWIFT
let seatA1 = "A1"
let seatA2 = "A2"
// ... 200 variables total

By seat #150, Excel crashed. Charlie realized: he needed programmatic seat number generation.

(2) The Range and Stride Solution

SWIFT
let rows = "ABCDEFGHIJKLMNOPQRST"
let seatsPerRow = 10
for row in rows {
    for seat in 1...seatsPerRow {
        print("\(row)\(seat)", terminator: " ")
    }
    print()
}

(3) Result: 5 hours → 3 seconds

Metric Dragging in Excel Swift Range Generation
200 seats 5 hours 3 seconds
Change seating layout Re-drag 10 minutes Change one number
Spaced seats Manual lookup stride generates directly
Error rate High 0

3. Range and ClosedRange

Range (half-open ..<) includes the start value but excludes the end. ClosedRange (closed ...) includes both endpoints.

100%
graph LR
    A["1...5"] --> B[1]
    A --> C[2]
    A --> D[3]
    A --> E[4]
    A --> F[5]
    G["1..<5"] --> H[1]
    G --> I[2]
    G --> J[3]
    G --> K[4]
Range Type Syntax Includes Example Result
ClosedRange a...b a, a+1, ..., b 1...3 -> 1,2,3
Range a..<b a, a+1, ..., b-1 1..<3 -> 1,2
PartialFrom a... a to infinity Array slice array[2...]
PartialThrough ...b ... to b Array slice array[...2]

(1) Creating and Traversing Ranges

SWIFT
// Closed range: includes 1 through 5
let closed = 1...5
for i in closed {
    print(i, terminator: " ")
}
print()
// Half-open range: includes 1 through 4
let halfOpen = 1..<5
for i in halfOpen {
    print(i, terminator: " ")
}
print()
// Reverse traversal
for i in (1...5).reversed() {
    print(i, terminator: " ")
}
print()

(2) Ranges in Array Slicing

SWIFT
let numbers = [10, 20, 30, 40, 50, 60, 70]
let firstThree = numbers[0..<3]
print(firstThree)
let fromSecond = numbers[2...]
print(fromSecond)
let firstFour = numbers[...3]
print(firstFour)

▶ Example: Exam Grading with Range Matching

SWIFT
// ============================================
// Using ranges in switch for grade classification
// ============================================
let scores = [95, 82, 67, 54, 43, 78, 91]
for score in scores {
    let grade: String
    switch score {
    case 90...100:
        grade = "A"
    case 80..<90:
        grade = "B"
    case 70..<80:
        grade = "C"
    case 60..<70:
        grade = "D"
    case 0..<60:
        grade = "F"
    default:
        grade = "Invalid"
    }
    print("Score \(score): Grade \(grade)")
}

Output:

TEXT 📖 للعرض فقط
Score 95: Grade A
Score 82: Grade B
Score 67: Grade D
Score 54: Grade F
Score 43: Grade F
Score 78: Grade C
Score 91: Grade A

4. Stride and Custom Sequences

Stride lets you traverse with a custom step value rather than incrementing by 1 every time. Custom sequences let you define your own iteration logic.

100%
graph TB
    A[Sequence] --> B["stride(from: 0, to: 10, by: 2)"]
    A --> C["stride(from: 10, through: 0, by: -2)"]
    B --> D["0, 2, 4, 6, 8"]
    C --> E["10, 8, 6, 4, 2, 0"]
Function Range Type Endpoint Inclusive Example
stride(from:to:by:) Half-open No stride(from:0, to:10, by:3) -> 0,3,6,9
stride(from:through:by:) Closed Yes stride(from:0, through:10, by:3) -> 0,3,6,9

(1) Basic Stride Usage

SWIFT
// 0 to 9, step 2
for i in stride(from: 0, to: 10, by: 2) {
    print(i, terminator: " ")
}
print()
// 10 to 0, step -3
for i in stride(from: 10, through: 0, by: -3) {
    print(i, terminator: " ")
}
print()
// Floating-point stepping
for temp in stride(from: 0.0, to: 1.0, by: 0.25) {
    print("\(Int(temp * 100))%", terminator: " ")
}
print()

(2) Custom Sequences

SWIFT
struct FibonacciSequence: Sequence {
    let count: Int
    func makeIterator() -> FibonacciIterator {
        return FibonacciIterator(count: count)
    }
}
struct FibonacciIterator: IteratorProtocol {
    let count: Int
    var current = 0
    var nextValue = 1
    var index = 0
    mutating func next() -> Int? {
        guard index < count else { return nil }
        defer { index += 1 }
        let result = current
        current = nextValue
        nextValue = result + current
        return result
    }
}
let fib = FibonacciSequence(count: 10)
for num in fib {
    print(num, terminator: " ")
}
print()

▶ Example: Temperature Conversion Table Generator

SWIFT
// ============================================
// Generate temperature conversion table with stride
// ============================================
print("Celsius\tFahrenheit")
print("-----------------")
for celsius in stride(from: 0.0, through: 100.0, by: 10.0) {
    let fahrenheit = celsius * 9 / 5 + 32
    print("\(Int(celsius))\t\(Int(fahrenheit))")
}
print()
print("Fahrenheit\tCelsius")
print("-------------------")
for f in stride(from: 212.0, through: 32.0, by: -20.0) {
    let c = (f - 32) * 5 / 9
    print("\(Int(f))\t\t\(Int(c))")
}

Output:

TEXT 📖 للعرض فقط
Celsius	Fahrenheit
-----------------
0		32
10		50
20		68
... (intermediate rows omitted)
100		212

Fahrenheit	Celsius
-------------------
212		100
192		88
... (intermediate rows omitted)
32		0

5. zip and Sequence Combination

zip pairs two sequences by index, producing a sequence of tuples. It stops automatically when either sequence ends.

Characteristic Description
Input Two sequences
Output Sequence of tuples (Element1, Element2)
Stop condition Whichever sequence ends first
Common uses Parallel traversal, data pairing, index generation

(1) Basic zip Usage

SWIFT
let names = ["Alice", "Bob", "Charlie", "Diana"]
let scores = [88, 92, 75, 95]
for (name, score) in zip(names, scores) {
    print("\(name): \(score)")
}
let short = ["A", "B", "C"]
let long = [1, 2, 3, 4, 5]
for pair in zip(short, long) {
    print(pair)
}

(2) zip + Range for Index Generation

SWIFT
let fruits = ["Apple", "Banana", "Orange", "Grape"]
for (index, fruit) in zip(1..., fruits) {
    print("\(index). \(fruit)")
}

▶ Example: Seat Assignment System

SWIFT
// ============================================
// Assign attendees to seats using zip and ranges
// ============================================
let attendees = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
let rows = ["A", "B", "C", "D"]
let seatsPerRow = 4
var allSeats: [String] = []
for row in rows {
    for seat in 1...seatsPerRow {
        allSeats.append("\(row)\(seat)")
    }
}
print("=== Seat Assignments ===")
for (attendee, seat) in zip(attendees, allSeats) {
    print("\(seat): \(attendee)")
}
let vipCount = 2
let vipSeats = allSeats.prefix(vipCount)
let vipAttendees = attendees.prefix(vipCount)
print("\n=== VIP Section ===")
for (person, seat) in zip(vipAttendees, vipSeats) {
    print("\(seat): \(person)")
}

Output:

TEXT 📖 للعرض فقط
=== Seat Assignments ===
A1: Alice
A2: Bob
A3: Charlie
A4: Diana
B1: Eve
B2: Frank

=== VIP Section ===
A1: Alice
A2: Bob

6. Full Example: Exam Seating Chart and Grade Analysis

SWIFT
// ============================================
// Exam seating chart and grade analysis
// Combining Range / Stride / zip / Sequence
// ============================================
import Foundation
let examRows = 5
let cols = 4
print("=== Exam Seating Chart ===")
for row in 0..<examRows {
    let rowLabel = Character(UnicodeScalar(65 + row)!)
    for col in 1...cols {
        print("\(rowLabel)\(col)", terminator: "\t")
    }
    print()
}
let students = ["Alice", "Bob", "Charlie", "Diana", "Eve",
                "Frank", "Grace", "Henry", "Ivy", "Jack",
                "Kevin", "Leo", "Mia", "Noah", "Olivia",
                "Paul", "Quinn", "Rose", "Sam", "Tina"]
let examSeats = examRows * cols
let assignedIndices = stride(from: 0, to: min(students.count, examSeats), by: 1)
print("\n=== Seat Assignments ===")
for (index, studentIndex) in zip(assignedIndices, 0..<students.count) {
    let row = index / cols
    let col = index % cols + 1
    let rowLabel = Character(UnicodeScalar(65 + row)!)
    print("\(rowLabel)\(col): \(students[studentIndex])")
}
print("\n=== Score Report ===")
var scores: [String: Int] = [:]
for student in students.prefix(examSeats) {
    scores[student] = Int.random(in: 40...100)
}
let gradeRanges: [ClosedRange<Int>: String] = [
    90...100: "A",
    80...89: "B",
    70...79: "C",
    60...69: "D",
    0...59: "F"
]
var gradeCount: [String: Int] = ["A": 0, "B": 0, "C": 0, "D": 0, "F": 0]
for (student, score) in scores.sorted(by: { $0.key < $1.key }) {
    var grade = "F"
    for (range, g) in gradeRanges {
        if range.contains(score) {
            grade = g
            break
        }
    }
    gradeCount[grade]! += 1
    print("\(student): \(score) -- \(grade)")
}
print("\n=== Grade Distribution ===")
for grade in ["A", "B", "C", "D", "F"] {
    let count = gradeCount[grade]!
    let bar = String(repeating: "#", count: count)
    print("\(grade): \(bar) (\(count))")
}

Output:

TEXT 📖 للعرض فقط
=== Exam Seating Chart ===
A1	A2	A3	A4
B1	B2	B3	B4
C1	C2	C3	C4
D1	D2	D3	D4
E1	E2	E3	E4

=== Seat Assignments ===
A1: Alice
A2: Bob
... (intermediate rows omitted)
E4: Tina

=== Score Report ===
Alice: 82 -- B
Bob: 67 -- D
... (intermediate rows omitted)

=== Grade Distribution ===
A: ### (3)
B: ##### (5)
C: #### (4)
D: ### (3)
F: # (1)

❓ FAQ

س What's the difference between ... and ..<?
ج a...b includes b (closed range). a..<b excludes b (half-open range). For example, 1...3 traverses 1,2,3; 1..<3 traverses 1,2.
س Can ranges be used with non-integer types?
ج Yes. Any type that conforms to Comparable works, such as Double or Character: 1.0...5.0 or "a"..."z". Note that Double ranges cannot be used with for-in (step ambiguity).
س Can the stride by parameter be negative?
ج Yes. A negative value creates a descending traversal. Remember to swap the from and to/through order — larger first, smaller second.
س How long is the sequence returned by zip?
ج The length of the shorter input sequence. zip stops when either sequence ends; it does not pad with nil.
س How do I check if a value falls within a range?
ج Use the contains() method: (1...10).contains(5) returns true. Or match in a switch: case 1...10:.
س Does array slicing with array[2...] copy the array?
ج No. Array slicing returns an ArraySlice that shares the original array's memory — no copy is made. The slice has its own index starting offset.

📖 Summary


📝 Exercises

  1. Beginner: Use 1...10 to print the 3rd row of a multiplication table (31 through 310), formatted as "3 x 1 = 3".
  2. Intermediate: Use stride to generate even numbers from 100 down to 0 with a step of -2, and calculate their sum.
  3. Challenge: Create a custom sequence WeekdaySequence that starts from a given weekday (e.g. "Wed") and loops indefinitely through ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]. Use zip to pair with the first 14 integers and print "Day 1: Wed", "Day 2: Thu"...
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%