Swift開発手法をポケモンで理解してみよう②

ポケモンニワカなのでポケモンに関する情報は正確ではない可能性があります、ご容赦ください。

OptionalやDelegate、その他概念等をポケモンに例えて覚えてみようとする試み。

Part2です。

Part2

try

挑戦するポケモン、運命を受け入れる!

解説

1. try

ピカチュウは電気を使って攻撃できますが、時には相手に避けられることもあります。tryは、成功するか失敗するか分からない挑戦をすることを意味します。

func catchPokemon() throws {
    // 捕まれる確率があるポケモン
    let success = Bool.random()
    if !success {
        throw NSError(domain: "捕まえられなかった", code: 1, userInfo: nil)
    }
}

do {
    try catchPokemon()
    print("ポケモンを捕まえた!")
} catch {
    print("捕まえられなかった…")
}

2. try!

リザードンは強いポケモンですが、時には失敗することもあります。try!は、成功することを確信して挑戦するが、失敗した場合はプログラムがクラッシュするものです。

func catchPokemon() throws {
    let success = false // 失敗する設定
    if !success {
        throw NSError(domain: "捕まえられなかった", code: 1, userInfo: nil)
    }
}

let result = try! catchPokemon() // ここでクラッシュする

3. try?

コイキングは弱いですが、時には大きな技が成功することもあります。try?は、成功するかどうか分からないが、失敗してもプログラムが続行するものです。

func catchPokemon() throws {
    let success = Bool.random()
    if !success {
        throw NSError(domain: "捕まえられなかった", code: 1, userInfo: nil)
    }
}

let result = try? catchPokemon() // 失敗してもプログラムは続行
if result == nil {
    print("捕まえられなかった…")
} else {
    print("ポケモンを捕まえた!")
}

~ それぞれの使用用途 ~

try

  • 使う時
    • エラーが発生する可能性があるが、エラー処理を行いたい場合。
    • 重要な処理で、エラーが発生した場合に適切に対処したい時。
  • 使うのは好ましくない時
    • エラーが発生することが予想されない場合(例: 事前に検証できる場合)。

try!

  • 使って良い時
    • 確実に成功することが分かっている場合(例: 事前に条件を確認している場合)。
    • テストコードやデモ用のコードで、エラー処理を省略したい場合。
  • 使ってはいけない時
    • エラーが発生する可能性がある処理で、プログラムがクラッシュするリスクを許容できない場合。

try?

  • 使って良い時
    • エラーが発生する可能性があるが、失敗してもプログラムを続行したい場合。
    • エラーが発生しても特に問題がない場合(例: オプショナルなデータ取得)。
  • 使ってはいけない時
    • エラーが発生した場合に必ず処理を行う必要がある場合(例: 重要なデータの取得や処理)。

Equatable, Comparable

ポケモンの個性を比べる

解説

1. Equatable

イシツブテは同じ形をしたポケモンがたくさんいるように、Equatableは、2つのオブジェクトが同じかどうかを比較するためのプロトコルで、イシツブテのように同じポケモンを見分けることができます。

struct Pokemon: Equatable {
    let name: String
    let level: Int
}

let pikachu1 = Pokemon(name: "ピカチュウ", level: 10)
let pikachu2 = Pokemon(name: "ピカチュウ", level: 10)

if pikachu1 == pikachu2 {
    print("同じポケモンだ!")
}

2. Comparable

リザードンはレベルや強さで他のポケモンと比較されるように、Comparableは、オブジェクトの大小を比較するためのプロトコルで、リザードンのように強さを比べることができます。

struct Pokemon: Comparable {
    let name: String
    let level: Int

    // lhs: 左辺のポケモン (比較対象の1つ)
    // rhs: 右辺のポケモン (比較対象のもう1つ)
    static func < (lhs: Pokemon, rhs: Pokemon) -> Bool {
        return lhs.level < rhs.level
    }
}

let charizard1 = Pokemon(name: "リザードン", level: 36)
let charizard2 = Pokemon(name: "リザードン", level: 50)

if charizard1 < charizard2 {
    print("リザードン1の方がレベルが低い!")
}

GetterとSetter

ポケモンのステータスを管理する

解説

1. getter (get)

フシギダネのHPを知りたいと仮定する。getterは、オブジェクトのプロパティの値を取得するための方法で、フシギダネのHPを確認するようなものです。

struct Pokemon {
    private var _hp: Int = 100

    // HPを取得するためのgetter
    var hp: Int {
        return _hp
    }
}

let bulbasaur = Pokemon()
print("フシギダネのHP: \(bulbasaur.hp)") // HPを取得

2. setter (set)

ピカチュウは新しい技を覚えたいとする。setterは、オブジェクトのプロパティの値を設定するための方法で、ピカチュウが新しい技を習得するようなもの。

struct Pokemon {
    private var _level: Int = 1

    // レベルを設定するためのsetter
    var level: Int {
        get {
            return _level
        }
        set {
            _level = newValue
        }
    }
}

var pikachu = Pokemon()
pikachu.level = 10 // レベルを設定
print("ピカチュウのレベル: \(pikachu.level)") // レベルを取得

Sequence Protocol

ポケモンの冒険を楽しむための道具 (要素への順次アクセス、要素を変換する)

解説

1. forEach

forEachは、それぞれのポケモンコレクションの各要素に対してアクションを実行する。

let pokemons = ["ピカチュウ", "リザードン", "フシギダネ"]
pokemons.forEach { pokemon in
    print("\(pokemon)が技を覚えた!")
}

2. filter

レベルが10以上のポケモンだけを選び出す。filterは、条件を満たす要素だけを抽出します。

let levels = [5, 10, 15, 3, 20]
let strongPokemons = levels.filter { $0 >= 10 }
print("レベル10以上のポケモン: \(strongPokemons)")

3. map

フシギダネのレベルを2倍にする。mapは、各要素に対して変換を行い、新しいコレクションを作成します。

let levels = [5, 10, 15]
let doubledLevels = levels.map { $0 * 2 }
print("レベルを2倍にしたポケモン: \(doubledLevels)")

4. flatMap

リザードンが複数の技を覚え、技のリストを一つにまとめる。flatMapは、ネストされたコレクションを平坦化し、要素を一つのコレクションにまとめます。

let moves = [["かえんほうしゃ", "ひこう"], ["かみつく", "いわくだき"]]
let allMoves = moves.flatMap { $0 }
print("リザードンの全技: \(allMoves)")

5. compactMap

進化するポケモンを選び出すが、進化しないポケモンは無視する。compactMapは、オプショナルな要素を取り除きながら変換を行う。

let pokemons: [String?] = ["ピカチュウ", nil, "リザードン", nil, "フシギダネ"]
let nonNilPokemons = pokemons.compactMap { $0 }
print("進化するポケモン: \(nonNilPokemons)")

6. reduce

サトシが仲間のポケモンのレベルを合計して、チームの強さを計算する。reduceは、コレクションの要素を一つの値にまとめます。

let levels = [5, 10, 15]
let totalLevel = levels.reduce(0, +)
print("チームの総レベル: \(totalLevel)")

Closure (クロージャ)

ポケモンの技を後で呼び出す魔法のノート

解説

1. クロージャーの基本

クロージャーは、特定の処理をまとめておき、必要な時に呼び出すことができます。

let thunderbolt = {
    print("ピカチュウが10万ボルトを放った!")
}

thunderbolt() // 技を使う

2. 引数を持つクロージャー

引数を持つクロージャーは、特定の値を受け取って処理を行うことができます。

let heal = { (hp: Int) in
    print("フシギダネがHPを\(hp)回復した!")
}

heal(20) // HPを回復

3. 戻り値を持つクロージャー

戦闘で技の威力を計算するように、戻り値を持つクロージャーは処理の結果を返すことができます。

let calculateDamage = { (attack: Int, defense: Int) -> Int in
    return attack - defense
}

let damage = calculateDamage(50, 20)
print("リザードンのダメージ: \(damage)")

4. クロージャーのキャプチャ

クロージャーは、外部の変数をキャプチャして使用することができます。

var level = 5
let levelUp = {
    level += 1
    print("コイキングのレベルが\(level)になった!")
}

levelUp() // レベルアップ