📚 Swift入門シリーズ: #10 辞書Dictionary → #11 セットSet
これまで学んだ配列、辞書、セットのすべての要素を処理したいとき、for-inループが非常に便利です。この記事では、反復処理の基本と応用を学びましょう。
来春からデータサイエンティストとして働く予定の技術オタク。
『知りたい』気持ちで質問を止められない、好奇心旺盛な学生。
前回はセットSetで重複のないデータを扱う方法を学んだね!
そうだね。第3章でコレクション型を学んできたね。今回は、それらのコレクションを効率的に処理する方法を学んでいくよ。
配列、辞書、セット...3つのコレクション型を学んできたね!
そうだね。今回は、それらのコレクションを効率的に処理する方法を学んでいくよ。
配列の全部の要素に同じ処理をしたいとき、一つ一つ書くしかないの?
いや、そんなことはないよ!for-inループ を使えば、コレクションのすべての要素に対して、同じ処理を簡単に実行できるんだ。
便利そう!教えて!
for-inループの基本
for-inループ は、コレクション(配列、辞書、セットなど)の要素を1つずつ取り出して処理する構文なんだ。基本的な書き方はこうなるよ。
for 要素 in コレクション {
// 処理
}
実際の例を見てみよう。
let fruits = ["りんご", "バナナ", "オレンジ"]
for fruit in fruits {
print(fruit)
}
// りんご
// バナナ
// オレンジ
for fruit in fruits は「fruitsの各要素をfruitという変数で受け取って、繰り返し処理する」という意味なんだ。
わかりやすい!fruitって変数は自分で決められるの?
そうだよ。ループ変数の名前は自由に決められるんだ。ただし、意味のある名前をつけた方がコードが読みやすくなるよ。
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
print(number)
}
// または
for n in numbers {
print(n)
}
単数形と複数形で対応させる(fruitsとfruit、numbersとnumber)のが一般的なスタイルなんだ。
配列のループ処理
配列のfor-inループをもう少し詳しく見ていこう。
基本的なループ
配列のすべての要素に対して処理を実行できるんだ。
let prices = [100, 200, 300]
var total = 0
for price in prices {
total += price
}
print("合計: \(total)円") // 合計: 600円
これで、配列のすべての要素を合計できたんだ。
インデックス付きループ
要素だけじゃなくて、何番目かも知りたいときはどうするの?
いい質問だね。enumerated() メソッドを使えば、インデックスと要素の両方を取得できるんだ。
let fruits = ["りんご", "バナナ", "オレンジ"]
for (index, fruit) in fruits.enumerated() {
print("\(index)番目: \(fruit)")
}
// 0番目: りんご
// 1番目: バナナ
// 2番目: オレンジ
enumerated() は、(インデックス, 要素) というタプルを返すんだ。(index, fruit) で、両方を同時に受け取っているんだよ。
おお!これは便利!
条件付きループ
ループの中で条件分岐を使うこともできるんだ。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for number in numbers {
if number % 2 == 0 {
print("\(number)は偶数")
} else {
print("\(number)は奇数")
}
}
これで、偶数と奇数を判定しながらループできるんだよ。
辞書のループ処理
辞書のfor-inループも見てみよう。
キーと値の両方を取得
辞書をループすると、キーと値のタプルが取得できるんだ。
let scores = [
"Math": 85,
"English": 92,
"Science": 78
]
for (subject, score) in scores {
print("\(subject): \(score)点")
}
// Math: 85点
// English: 92点
// Science: 78点
(subject, score) で、キーと値を同時に受け取っているんだ。変数名は自由に決められるよ。
キーだけ、値だけを取得
キーだけ、または値だけが欲しい場合は、keys や values プロパティを使うんだ。
let scores = [
"Math": 85,
"English": 92,
"Science": 78
]
// キーだけ
print("科目一覧:")
for subject in scores.keys {
print(subject)
}
// 値だけ
var total = 0
for score in scores.values {
total += score
}
print("合計点: \(total)点")
セットのループ処理
セットのループも、配列と同じように書けるんだ。
let tags: Set<String> = ["Swift", "iOS", "Programming"]
for tag in tags {
print("#\(tag)")
}
// #Swift
// #iOS
// #Programming
// (順序は不定)
ただし、セットは順序が保証されていないから、毎回違う順序で処理される可能性があるんだよ。
範囲のループ処理
コレクションじゃなくて、単純に「1から10まで繰り返す」みたいなこともできるの?
できるよ!範囲演算子 を使えば、数値の範囲でループできるんだ。
閉範囲演算子(...)
1...5 で「1から5まで(5を含む)」という範囲を表すんだ。
for i in 1...5 {
print(i)
}
// 1
// 2
// 3
// 4
// 5
これで、1から5まで5回ループするんだよ。
半開範囲演算子(..<)
..< を使うと、終端を含まない範囲になるんだ。
for i in 0..<5 {
print(i)
}
// 0
// 1
// 2
// 3
// 4
0..<5 は「0から4まで」という意味なんだ。配列のインデックスとして使いやすいんだよ。
let fruits = ["りんご", "バナナ", "オレンジ"]
for i in 0..<fruits.count {
print("\(i): \(fruits[i])")
}
// 0: りんご
// 1: バナナ
// 2: オレンジ
ループ変数を使わない場合
ループ変数が必要ない場合はどうするの?単純に10回繰り返したいだけとか。
その場合は、アンダースコア _ を使うんだ。
for _ in 1...3 {
print("Hello")
}
// Hello
// Hello
// Hello
_ は「この値は使わない」という意味なんだよ。
stride で複雑なループ
stride 関数を使えば、もっと複雑なループができるんだ。
一定間隔で進む
for i in stride(from: 0, to: 10, by: 2) {
print(i)
}
// 0
// 2
// 4
// 6
// 8
stride(from:to:by:) は、fromからtoまで(toを含まない)、byの間隔で進むんだ。
逆順にループ
for i in stride(from: 5, through: 1, by: -1) {
print(i)
}
// 5
// 4
// 3
// 2
// 1
stride(from:through:by:) は、throughで指定した値を含むんだよ。
ループの制御
ループの途中で抜けたり、スキップしたりすることはできるの?
できるよ!break と continue という制御文があるんだ。これは次の章で詳しく学ぶけど、簡単に紹介するね。
break:ループを抜ける
break を使うと、ループを途中で終了できるんだ。
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
if number == 3 {
break // 3になったらループを抜ける
}
print(number)
}
// 1
// 2
3に到達した時点でループが終了するんだよ。
continue:次の繰り返しにスキップ
continue を使うと、その回の処理をスキップして次の繰り返しに進むんだ。
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
if number == 3 {
continue // 3の時はスキップ
}
print(number)
}
// 1
// 2
// 4
// 5
3の時だけ print がスキップされるんだよ。
ネストしたループ
ループの中にループを入れることってできるの?
できるよ!これを ネストしたループ って言うんだ。
for i in 1...3 {
for j in 1...3 {
print("(\(i), \(j))")
}
}
// (1, 1)
// (1, 2)
// (1, 3)
// (2, 1)
// (2, 2)
// (2, 3)
// (3, 1)
// (3, 2)
// (3, 3)
外側のループが1回回るごとに、内側のループが3回回るんだ。
九九の表を作る
ネストしたループの実践例として、九九の表を作ってみよう。
for i in 1...9 {
for j in 1...9 {
let result = i * j
print("\(i) × \(j) = \(result)")
}
print("---") // 区切り線
}
これで、1の段から9の段まで全部出力できるんだよ。
ループのパフォーマンス
ループを書くときの、パフォーマンスに関するポイントも知っておこう。
ループの外に出せる処理
ループの中で毎回同じ計算をするのは無駄なんだ。
// 悪い例
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
let multiplier = 10 // 毎回同じ値を計算している
print(number * multiplier)
}
// 良い例
let numbers = [1, 2, 3, 4, 5]
let multiplier = 10 // ループの外に出す
for number in numbers {
print(number * multiplier)
}
ループの外に出せる処理は、外に出した方が効率的なんだよ。
countの取得
配列の count もループの外で取得しておくと良いことがあるんだ。
let fruits = ["りんご", "バナナ", "オレンジ"]
let count = fruits.count // 一度だけ取得
for i in 0..<count {
print(fruits[i])
}
ただし、Swiftのコンパイラは賢いから、多くの場合は最適化してくれるんだけどね。
実践練習
じゃあ、練習問題をやってみよう。
問題1: 1から100までの数値で、3の倍数だけを出力するプログラムを書いてみて。
やってみる!
for number in 1...100 {
if number % 3 == 0 {
print(number)
}
}
できた!
完璧だね!剰余演算子を使って3の倍数を判定しているね。
問題2: 配列の要素とそのインデックスを使って、「0番目: りんご」という形式で出力してみて。
let fruits = ["りんご", "バナナ", "オレンジ"]
for (index, fruit) in fruits.enumerated() {
print("\(index)番目: \(fruit)")
}
こう?
素晴らしい!enumerated() を使いこなせているね。
最後に、少し難しい問題をやってみよう。
問題3: 九九の表で、答えが偶数のものだけを出力してみて。
えーと、ネストしたループで...
for i in 1...9 {
for j in 1...9 {
let result = i * j
if result % 2 == 0 {
print("\(i) × \(j) = \(result)")
}
}
}
こうかな?
完璧だよ!ネストしたループと条件分岐を組み合わせて、偶数の結果だけを出力できているね。素晴らしい!
まとめ
この記事では、for-inループによる反復処理について学びました。
for-inループの基本:
for 要素 in コレクション { 処理 }- 配列、辞書、セット、範囲など様々なものに使える
配列のループ:
- 基本:
for item in array - インデックス付き:
for (index, item) in array.enumerated()
辞書のループ:
- キーと値:
for (key, value) in dictionary - キーのみ:
for key in dictionary.keys - 値のみ:
for value in dictionary.values
セットのループ:
for item in set(順序は不定)
範囲のループ:
- 閉範囲:
for i in 1...5(1から5まで) - 半開範囲:
for i in 0..<5(0から4まで) - stride:
stride(from:to:by:)、stride(from:through:by:)
ループの制御:
- break:ループを抜ける
- continue:次の繰り返しにスキップ
ネストしたループ:
- ループの中にループを書ける
- 外側が1回回るごとに内側が全回転する
次回から第4章「プログラムの流れを操る」に入ります。記事13「もし〜なら」として、if文による条件分岐について学びましょう!