Swift: Swiftの範囲とシーケンス:Range、ClosedRange、Stride

範囲は数直線上の区間のようなものです——AからBまでの連続した整数。このレッスンではSwiftのすべての範囲型とシーケンス型の作成、使用、カスタマイズ方法を学びます。

1. 学習目標


2. イベントプランナーの実話

(1) 課題:200席番号の手動生成 — Excelがフリーズ

Charlieは200人規模の技術カンファレンスを企画しています。座席番号を生成する必要があります:A列からT列(20列)、各列10席、参加者を座席に割り当て、座席表を作成します。 彼はExcelのフィルハンドルをドラッグして始めましたが、��IP席を1つ追加するたびにシート全体を再調整する必要がありました:

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

150席目までにExcelがクラッシュしました。Charlieは気づきました:プログラムによる座席番号生成が必要だ。

(2) 範囲とStrideの解決策

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

(3) 結果:5時間 → 3秒

指標 Excelのドラッグ Swiftの範囲生成
200席 5時間 3秒
座席レイアウト変更 再ドラッグ10分 数字を1つ変えるだけ
間隔を空けた席 手動検索 strideが直接生成
エラー率 高い 0

3. RangeとClosedRange

Range(半開 ..<)は開始値を含みますが終了値を含みません。ClosedRange(閉 ...)は両方の端点を含みます。

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]
範囲型 構文 含む値 結果例
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から無限 配列スライス array[2...]
PartialThrough ...b ...からbまで 配列スライス array[...2]

(1) 範囲の作成と走査

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) 配列スライスでの範囲

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)

▶ サンプル: 範囲マッチングを使った試験の採点

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)")
}

出力:

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とカスタムシーケンス

Strideを使うと毎回1ずつ増やすのではなく、カスタムのステップ値で走査できます。カスタムシーケンスを使うと独自の反復ロジックを定義できます。

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"]
関数 範囲型 端点を含むか
stride(from:to:by:) 半開 いいえ stride(from:0, to:10, by:3) -> 0,3,6,9
stride(from:through:by:) はい stride(from:0, through:10, by:3) -> 0,3,6,9

(1) Strideの基本的な使い方

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) カスタムシーケンス

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()

▶ サンプル: 温度変換表ジェネレーター

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))")
}

出力:

TEXT 📖 参照専用
摂氏	華氏
-----------------
0		32
10		50
20		68
... (中間行は省略)
100		212

華氏	摂氏
-------------------
212		100
192		88
... (中間行は省略)
32		0

5. zipとシーケンスの組み合わせ

zipは2つのシーケンスをインデックスでペアにし、タプルのシーケンスを生成します。どちらかのシーケンスが終了すると自動的に停止します。

特性 説明
入力 2つのシーケンス
出力 タプル (要素1, 要素2) のシーケンス
停止条件 最初に終了したシーケンス
一般的な用途 並行走査、データペアリング、インデックス生成

(1) zipの基本的な使い方

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 + 範囲

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

▶ サンプル: 座席割り当てシステム

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)")
}

出力:

TEXT 📖 参照専用
=== 座席割り当て ===
A1: Alice
A2: Bob
A3: Charlie
A4: Diana
B1: Eve
B2: Frank

=== VIP席 ===
A1: Alice
A2: Bob

6. 完全な例:試験座席表と成績分析

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))")
}

出力:

TEXT 📖 参照専用
=== 試験座席表 ===
A1	A2	A3	A4
B1	B2	B3	B4
C1	C2	C3	C4
D1	D2	D3	D4
E1	E2	E3	E4

=== 座席割り当て ===
A1: Alice
A2: Bob
... (中間行は省略)
E4: Tina

=== 成績レポート ===
Alice: 82 -- B
Bob: 67 -- D
... (中間行は省略)

=== 成績分布 ===
A: ### (3)
B: ##### (5)
C: #### (4)
D: ### (3)
F: # (1)

❓ よくある質問

Q .....<の違いは?
A a...bはbを含みます(閉範囲)。a..<bはbを含みません(半開範囲)。例えば、1...3は1,2,3を走査し、1..<3は1,2を走査します。
Q 範囲は非整数型にも使えますか?
A はい。Comparableに準拠する任意の型(DoubleやCharacterなど)で動作します:1.0...5.0や"a"..."z"。ただし、Double範囲はfor-inでは使えません(ステップの曖昧さのため)。
Q strideのbyパラメータは負の値にできますか?
A はい。負の値は降順の走査を作成します。fromとto/throughの順序を入れ替えることを忘れないでください——大きい方を先に、小さい方を後にします。
Q zipが返すシーケンスの長さは?
A 短い方の入力シーケンスの長さです。zipはどちらかのシーケンスが終了すると停止し、nilでパディングはしません。
Q 値が範囲内にあるかを確認するには?
A contains()メソッドを使用します:(1...10).contains(5)はtrueを返します。またはswitchでマッチします:case 1...10:
Q array[2...]での配列スライスは配列をコピーしますか?
A いいえ。配列スライスは元の配列のメモリを共有するArraySliceを返します——コピーは行われません。スライスは独自のインデックス開始オフセットを持ちます。

📖 まとめ


📝 練習問題

  1. 初級: 1...10を使って九九の3の段(31から310)を「3 x 1 = 3」の形式で出力してください。
  2. 中級: strideを使って100から0までの偶数を-2のステップで生成し、それらの合計を計算してください。
  3. 上級: カスタムシーケンスWeekdaySequenceを作成し、与えられた曜日(例:「水」)から始めて["月", "火", "水", "木", "金", "土", "日"]を無限にループさせてください。zipを使って最初の14個の整数とペアにして「1日目: 水」、「2日目: 木」...と出力してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%