📚 Swift入門シリーズ: #14 switch文 → #15 whileループ
前回はwhileループによる条件に基づいた繰り返し処理を学びました。今回は、コードをより安全で読みやすくするguard文について学びましょう。
来春からデータサイエンティストとして働く予定の技術オタク。
『知りたい』気持ちで質問を止められない、好奇心旺盛な学生。
前回はwhileループで条件に基づく繰り返し処理を学んだね!
そうだね。今回は、Swiftの特徴的な機能の1つ、guard文 について学んでいくよ。これはコードを安全で読みやすくするための重要な機能なんだ。
guard?それって何?if文とは違うの?
guard文は、「条件が満たされていない場合に早期に処理を抜ける」ための構文なんだ。if文と似ているけど、使い方と目的が違うんだよ。
guard文の基本
guard文 の基本的な書き方はこうなるよ。
guard 条件 else {
// 条件がfalseの場合の処理
// ここで必ず処理を抜ける(return, break, continueなど)
}
// 条件がtrueの場合だけ、ここに到達する
guard文の特徴は、elseブロックで必ず処理を抜けなければならない ということなんだ。
「必ず処理を抜ける」ってどういうこと?
elseブロックの中では、return、break、continue、throw、あるいは fatalError() のような「制御を転送する文」を書かないとエラーになるんだ。
func checkAge(age: Int) {
guard age >= 18 else {
print("未成年です")
return // これがないとエラー
}
print("成人です")
}
checkAge(age: 20) // 成人です
checkAge(age: 15) // 未成年です
この例では、18歳未満の場合は早期に関数から抜けて、18歳以上の場合だけ「成人です」と表示されるんだよ。
if文との違い
これってif文でも書けるんじゃない?
その通り!同じことはif文でも書けるんだ。でも、guard文の方が 意図が明確 で 読みやすい 場合があるんだよ。
if文での書き方:
func processUser(name: String?) {
if name != nil {
let unwrappedName = name!
print("こんにちは、\(unwrappedName)さん")
// さらに処理が続く...
} else {
print("名前が不正です")
return
}
}
guard文での書き方:
func processUser(name: String?) {
guard let unwrappedName = name else {
print("名前が不正です")
return
}
print("こんにちは、\(unwrappedName)さん")
// さらに処理が続く...
}
guard文の方が、「名前が不正な場合は早期に抜ける」という意図が明確だよね。
確かに!guard文の方が分かりやすい!
さらに重要なのは、guard文でアンラップした変数は、guard文の後でも使える んだ。
func greet(name: String?) {
guard let name = name else {
print("名前がありません")
return
}
// ここでnameが使える!
print("こんにちは、\(name)さん")
print("\(name)さん、ようこそ!")
}
if letでアンラップした場合は、ifブロックの中でしか使えないんだよ。
func greet(name: String?) {
if let name = name {
// ここでしかnameが使えない
print("こんにちは、\(name)さん")
}
// ここではnameは使えない
// print("\(name)さん、ようこそ!") // エラー
}
guard文が向いている場面
guard文は、特に 前提条件のチェック に向いているんだ。
1. 関数の引数チェック
func divide(a: Int, b: Int) -> Int? {
guard b != 0 else {
print("0で割ることはできません")
return nil
}
return a / b
}
if let result = divide(a: 10, b: 2) {
print("結果: \(result)") // 結果: 5
}
if let result = divide(a: 10, b: 0) {
print("結果: \(result)")
} else {
print("計算できませんでした") // これが表示される
}
2. 複数の条件チェック
複数の条件をチェックする場合、guard文を使うとネストが浅くなって読みやすくなるんだ。
if文での書き方(ネストが深い):
func processOrder(productId: String?, quantity: Int?, userId: String?) {
if let productId = productId {
if let quantity = quantity {
if quantity > 0 {
if let userId = userId {
print("注文処理: 商品\(productId)を\(quantity)個、ユーザー\(userId)が注文")
} else {
print("エラー: ユーザーIDがありません")
return
}
} else {
print("エラー: 数量が不正です")
return
}
} else {
print("エラー: 数量がありません")
return
}
} else {
print("エラー: 商品IDがありません")
return
}
}
guard文での書き方(フラットで読みやすい):
func processOrder(productId: String?, quantity: Int?, userId: String?) {
guard let productId = productId else {
print("エラー: 商品IDがありません")
return
}
guard let quantity = quantity else {
print("エラー: 数量がありません")
return
}
guard quantity > 0 else {
print("エラー: 数量が不正です")
return
}
guard let userId = userId else {
print("エラー: ユーザーIDがありません")
return
}
print("注文処理: 商品\(productId)を\(quantity)個、ユーザー\(userId)が注文")
}
guard文を使うと、ネストが深くならず、条件チェックが段階的に行われるのが分かりやすいよね。
すごい!全然読みやすさが違う!
3. 複数の条件を1つのguardで
複数の条件を1つのguard文にまとめることもできるんだ。
func login(username: String?, password: String?) {
guard let username = username,
let password = password,
!username.isEmpty,
!password.isEmpty else {
print("ユーザー名とパスワードを入力してください")
return
}
print("\(username)でログインしました")
}
login(username: "user1", password: "pass123") // user1でログインしました
login(username: nil, password: "pass123") // ユーザー名とパスワードを入力してください
login(username: "user1", password: "") // ユーザー名とパスワードを入力してください
カンマで区切って、複数の条件を並べられるんだ。すべての条件がtrueの場合だけ、guard文を通過できるんだよ。
guard文とループ
guard文は、ループの中でも使えるんだ。ループの中では、continue や break を使って処理を抜けることができるよ。
let numbers = [5, -2, 8, 0, -3, 12]
for number in numbers {
guard number > 0 else {
print("\(number)は正の数ではないのでスキップ")
continue
}
print("\(number)の2倍は\(number * 2)")
}
// 5の2倍は10
// -2は正の数ではないのでスキップ
// 8の2倍は16
// 0は正の数ではないのでスキップ
// -3は正の数ではないのでスキップ
// 12の2倍は24
guard文で条件を満たさない場合は continue でスキップして、条件を満たす場合だけ処理を実行するんだ。
これは便利!条件に合うものだけ処理したいときに使えるね!
その通り!もう1つ例を見てみよう。
let optionalNumbers: [Int?] = [1, nil, 3, nil, 5]
for optionalNumber in optionalNumbers {
guard let number = optionalNumber else {
print("nilをスキップ")
continue
}
print("数値: \(number)")
}
// 数値: 1
// nilをスキップ
// 数値: 3
// nilをスキップ
// 数値: 5
nilの要素をスキップして、値がある要素だけ処理できるんだよ。
if文とguard文の使い分け
if文とguard文、どっちを使えばいいか迷っちゃう...
使い分けのガイドラインを教えるね。
guard文を使う場面:
- 前提条件のチェック
- 「この条件を満たさない場合は処理を続けられない」とき
- オプショナルをアンラップして、その後も使いたいとき
- ネストを浅くしたいとき
if文を使う場面:
- 条件によって異なる処理を実行したいとき
- 条件がtrueの場合の処理が中心のとき
- アンラップした値をそのブロック内だけで使いたいとき
// guard文が向いている例
func validateUser(age: Int?) {
guard let age = age else {
print("年齢が入力されていません")
return
}
guard age >= 0 else {
print("年齢が不正です")
return
}
// ここからメインの処理
print("年齢: \(age)歳")
}
// if文が向いている例
func getAgeCategory(age: Int) -> String {
if age < 18 {
return "未成年"
} else if age < 65 {
return "成人"
} else {
return "高齢者"
}
}
guard文は 早期リターン のため、if文は 条件分岐 のため、と覚えておくといいよ。
実践練習
じゃあ、練習問題をやってみよう。
問題1: ユーザー登録の関数を作ってみて。名前(name)、年齢(age)、メールアドレス(email)をオプショナルで受け取って、すべて有効な値があれば「登録完了」、どれか1つでもnilなら「入力エラー」と表示する関数を、guard文を使って書いてみて。
やってみる!
func registerUser(name: String?, age: Int?, email: String?) {
guard let name = name else {
print("入力エラー: 名前がありません")
return
}
guard let age = age else {
print("入力エラー: 年齢がありません")
return
}
guard let email = email else {
print("入力エラー: メールアドレスがありません")
return
}
print("登録完了: \(name)さん(\(age)歳、\(email))")
}
registerUser(name: "田中", age: 25, email: "tanaka@example.com")
// 登録完了: 田中さん(25歳、tanaka@example.com)
registerUser(name: "佐藤", age: nil, email: "sato@example.com")
// 入力エラー: 年齢がありません
できた!
完璧だね!guard文を使って、段階的に条件をチェックできているよ。
さらに、複数の条件を1つのguard文にまとめることもできるよ。
func registerUser(name: String?, age: Int?, email: String?) {
guard let name = name,
let age = age,
let email = email else {
print("入力エラー: すべての項目を入力してください")
return
}
print("登録完了: \(name)さん(\(age)歳、\(email))")
}
どちらの書き方も正しいよ。エラーメッセージを細かく分けたいなら最初の書き方、シンプルにしたいなら2番目の書き方がいいね。
なるほど!目的に応じて使い分けられるんだね!
その通り!
問題2: 配列の中から正の偶数だけを取り出して2倍にする処理を、guard文とcontinueを使って書いてみて。
えーと...
let numbers = [3, 8, -2, 10, -5, 6, 0, 12]
for number in numbers {
guard number > 0 else {
continue
}
guard number % 2 == 0 else {
continue
}
print("\(number)の2倍は\(number * 2)")
}
// 8の2倍は16
// 10の2倍は20
// 6の2倍は12
// 12の2倍は24
こう?
素晴らしい!guard文を使って、条件を満たさないものを早期にスキップできているね。
複数の条件を1つのguard文にまとめることもできるよ。
let numbers = [3, 8, -2, 10, -5, 6, 0, 12]
for number in numbers {
guard number > 0, number % 2 == 0 else {
continue
}
print("\(number)の2倍は\(number * 2)")
}
この場合は、「正の数かつ偶数」という2つの条件を1つのguard文でチェックしているんだ。どちらの書き方も正しいから、読みやすい方を選べばいいよ。
まとめ
この記事では、guard文について学びました。
guard文の基本:
guard 条件 else { 処理を抜ける }- 条件がfalseの場合に早期リターン
- elseブロックで必ず処理を抜ける必要がある
guard文の特徴:
- アンラップした変数をguard文の後でも使える
- ネストを浅くできる
- 前提条件のチェックに適している
if文との使い分け:
- guard文:早期リターン、前提条件チェック
- if文:条件分岐、異なる処理の実行
複数の条件:
- カンマで区切って複数の条件を並べられる
- すべての条件がtrueの場合だけ通過
ループとの組み合わせ:
continueでスキップbreakでループを抜ける
重要なポイント:
- コードの可読性を高める
- 意図を明確にする
- ネストを減らしてフラットな構造にする
次回は「nil安全」として、オプショナル型について詳しく学びます。Swiftの安全性を支える重要な機能を理解しましょう!