【Swift】class

class とは、構造体と同様に複数のプロパティやメソッドをまとめて保持できるものです。
class は設計図と表現されていることが多いかと思いますが、必要なときに インスタンス化( 設計図から実体に ) することで使用することができるようになります。

前回は switch文 について書きました。

今回は class について書いていこうと思いますが、 class をやる前に 構造体 を理解した上でこの記事を見た方が理解しやすいと思います。

構造体、プロパティ、イニシャライザ、メソッドなどについてこちらの記事で紹介しています。

classとは

class とは、構造体と同様に複数のプロパティやメソッドをまとめて保持できるものです。
class は設計図と表現されていることが多いかと思いますが、必要なときに インスタンス化( 設計図から実体に ) することで使用することができるようになります。

class の概要

  • 構造体と似ているが、構造体は 値型 であるのに対し、class参照型 という違いがある。
  • 構造体や列挙型とは異なり 継承 することができる。
  • 構造体では、イニシャライザを定義しなくてもデフォルトのメンバーワイズイニシャライザで初期化されるが、class ではプロパティの初期値が無い状態で、イニシャライザが無いとエラーになる。
  • クラスやクラスの継承はオブジェクト指向型の言語ではとても重要。

※ 継承とは、すでに定義してある class を引き継いで、新しい class を定義する仕組み。

classの書式

 class クラス名{
     プロパティ定義
     イニシャライザ定義
     メソッド定義
 }

class キーワードを書いた後に、クラス名 を書きます。
{ } の中にプロパティ、イニシャライザ、メソッドを定義していきます。

classの定義例

class Fellow{
    
    //プロパティ
    let name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }
    
    //メソッド
    func printFellow(){
        print("\(name)のステータス")
        print("LV:\(level)")
        print("職業:\(profession ?? "無職")")
    }

}

//インスタンス化
var arusu = Fellow(name: "アルス", level: 20, profession: "バトルマスター")

//実行
arusu.printFellow()

上のコードについて解説↓

class名

class Fellow{
}

class の名前は Swift で推奨されているアッパーキャメルケースで書きます。
命名規則についてはこちらの記事で紹介しています。

プロパティ

class Fellow{

    //プロパティ
    let name:String
    var level:Int
    var profession:String?

}

Fellow クラスのプロパティには、変数を 3 つ宣言していますが初期値は定義していません。

イニシャライザ

class Fellow{

    //プロパティ
    var name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }

}

イニシャライザの引数を 3 つ( プロパティの数だけ )書いて、後でインスタンス化するときに全てのプロパティに初期値を入力して初期化します。
このとき、Optional型 のプロパティがあった場合は、初期値を入力しなくてもデフォルトで nil が代入されるのでイニシャライザに書くことは必須ではありません。

※ 構造体では、イニシャライザを定義しなくてもデフォルトのメンバーワイズイニシャライザで初期化されるが、class ではプロパティの初期値が無い状態で、イニシャライザが無いとエラーになります。

メソッド

class Fellow{

    //プロパティ
    var name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }
    
    //メソッド
    func printFellow(){
        print("\(name)のステータス")
        print("LV:\(level)")
        print("職業:\(profession ?? "無職")")
    }

}

インスタンス化したときの、各プロパティの値を出力するメソッドを定義しました。

インスタンス化

class Fellow{

    //プロパティ
    var name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }
    
    //メソッド
    func printFellow(){
        print("\(name)のステータス")
        print("LV:\(level)")
        print("職業:\(profession ?? "無職")")
    }

}

//インスタンス化
var arusu = Fellow(name: "アルス", level: 20, profession: "バトルマスター")

Fellow クラスの引数に初期値を入力して、変数 arusu に代入します。
これで変数 arusuFellow クラスのインスタンスとして使えるようになりました。

インスタンスのメソッドを実行

入力した初期値がプロパティに反映されているか確認したいので、インスタンスのメソッドを実行します。

class Fellow{

    //プロパティ
    var name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }
    
    //メソッド
    func printFellow(){
        print("\(name)のステータス")
        print("LV:\(level)")
        print("職業:\(profession ?? "無職")")
    }

}

//インスタンス化
var arusu = Fellow(name: "アルス", level: 20, profession: "バトルマスター")

//メソッドを実行
arusu.printFellow()

//実行結果
/*
アルスのステータス
LV:20
職業:バトルマスター
*/

実行した結果、インスタンス化するときに引数に入力した値がきちんと反映されているので、変数 arusuFellow クラスのインスタンスとして振る舞っている事がわかります。

classコードimg

classは参照型

class は構造体の値型とは違い、参照型 です。

値型と参照型の違い

値型はデータ自体がコピーされるのに対し、
参照型はデータ自体をコピーするのではなく、参照先の「場所」( データが格納されている場所 )をコピーします。
なので同じインスタンスが 2 つあった場合、片方の値を変更するともう片方の値も同じになります。

~ ~ ~
//インスタンス化
var arusu = Fellow(name: "アルス", level: 20, profession: "バトルマスター")

//メソッドを実行
arusu.printFellow()

//classは参照型
//クラスFellowのインスタンスである変数arusuを、変数rotoにコピー
var roto = arusu

//プロパティの値を変更
roto.name = "ロト"

//rotoのメソッドを実行
roto.printFellow()

//arusuのメソッドを実行
arusu.printFellow()

//実行結果
/*
 アルスのステータス
 LV:20
 職業:バトルマスター

 ロトのステータス
 LV:20
 職業:バトルマスター

 ロトのステータス
 LV:20
 職業:バトルマスター
*/

Fellow クラスのインスタンスである変数 arusu を変数 roto にコピーしてインスタンスを 2 つにします。
インスタンス roto の方だけ、プロパティ name の値を変更して、両方のインスタンスからメソッドを呼び実行してみます。
実行結果を見ると、インスタンス roto の方は name の値を変更したので当然変更されていますが、
インスタンス arusu の方は変更していないのに変更されています。
つまり同じインスタンスを複数作っても、インスタンスのデータ自体は 1 つしかなく、データが格納されている「場所」 をコピーしているということがわかると思います。

classコードimg2

classの継承

class の継承とは、新しく class を定義するときにすでに定義されている class のプロパティやメソッドなどを引き継ぐことができる仕組みです。
継承元の定義を利用できるため、新しい class を定義する際に、継承することでコードの量を減らせるなど労力を小さくできるメリットがあります。
継承元になる class をスーパークラス( 親クラス )、継承先の class をサブクラス( 子クラス )と言います。

class を継承することでできること

  • サブクラスは、スーパークラスのプロパティやメソッドを使用できる。
  • サブクラスでは、プロパティやイニシャライザ、メソッドを新しく定義できる。
  • スーパークラスのスーパークラス、サブクラスのサブクラスとなっていっても継承できる。
  • サブクラスでは、継承したプロパティやメソッドなどを オーバーライド ( override )する事ができる。

オーバーライドとは、スーパークラスのプロパティやメソッドの 上書き をすることです。オーバーロードというものもありますが、全く違うものなので注意。

継承の書式

 サブクラス名 : スーパークラス名 {
 プロパティ定義
 イニシャライザ定義
 メソッド定義
 }

サブクラスの後に : を書いて継承したいクラス名を書きます。

クラス継承の例

上の例で定義した Fellow クラスを使って新しく定義するクラスに継承します。

class Fellow{
    ~ ~ ~
}

    ~ ~ ~

//classの継承
class Monster: Fellow{
    
    //プロパティ
    var skill:String
    
    //イニシャライザ
    init(name: String, level: Int, skill: String){
        self.skill = skill
        //スーパークラスのイニシャライザを流用
        super.init(name: name, level: level, profession: nil)
    }
    
    //スーパークラスのメソッドをオーバーライド
    override func printFellow() {
        print("\(name)のステータス")
        print("LV:\(level)")
        print("特技:\(skill)")
    }
}

//インスタンス化してメソッド実行
var suraimu = Monster(name: "スライム", level: 18, skill: "メラ")
suraimu.printFellow()

Fellow クラスを継承した、Monster クラスを定義しています。
スーパークラスが Fellow 、サブクラスが Monster となります。
これで、Fellow クラスのプロパティやメソッドなどが Monster クラスでも使えるようになりました。

Monster クラスのプロパティには、新しく変数を 1 つ宣言しています。

var skill:String

次にイニシャライザでプロパティの初期化をしていきます。
引数は namelevelskill の 3 つ用意します。

init(name: String, level: Int, skill: String){
}

まず、新しく宣言したプロパティを先に初期化します。

init(name: String, level: Int, skill: String){
    self.skill = skill
}

プロパティ skill をイニシャライザと紐付けました。
残りの namelevel ですが、スーパークラスですでに定義してあるイニシャライザを使いたいと思います。

init(name: String, level: Int, skill: String){
    self.skill = skill
    super.init(name: name, level: level, profession: nil)
}

スーパークラス Fellow で定義してあるイニシャライザの引数 namelevel をサブクラス Monster で定義したイニシャラザの引数 namelevel を紐付けます。
super. キーワードを頭に付けることで、スーパークラスのイニシャライザを使うことができます。
Monster クラスでは、引数 profession は使わないので nil としています。
これで引数 namelevelskill の 3 つに値を入力することで、Monster クラスをインスタンス化することができます。

次に Fellow クラスにすでに定義してあるメソッドを使って、引数に入力された値がプロパティに反映されているか確認します。
Fellow クラスのメソッド printFellow は プロパティの namelevelprofession に代入されている値を出力するだけのメソッドですが、そのまま使うとMonster クラスでは必要ない profession の値も出力もしてしまうので、Monster クラスで定義したプロパティ skill の値を出力するようにメソッドを変更します。

override func printFellow() {
    print("\(name)のステータス")
    print("LV:\(level)")
    print("特技:\(skill)")
}

override キーワードの後にスーパークラスで定義済みのメソッドを書くと、処理を上書きすることができます。
※ オーバーロードと間違わないように注意。

インスタンス化してメソッドを実行します。

var suraimu = Monster(name: "スライム", level: 18, skill: "メラ")
suraimu.printFellow()

//実行結果
/*
スライムのステータス
LV:18
特技:メラ
*/

namelevelskill の値を出力することができました。
Monster クラスで定義していないプロパティ namelevel にアクセス出来ているので、スーパークラスのプロパティが継承されているということがわかると思います。

classコードimg3

全コード

class Fellow{
    
    //プロパティ
    var name:String
    var level:Int
    var profession:String?
    
    //イニシャライザ
    init(name:String, level:Int, profession: String?){
        self.name = name
        self.level = level
        self.profession = profession
    }
    
    //メソッド
    func printFellow(){
        print("\(name)のステータス")
        print("LV:\(level)")
        print("職業:\(profession ?? "無職")")
    }
    
}

//インスタンス化
var arusu = Fellow(name: "アルス", level: 20, profession: "バトルマスター")

//メソッドを実行
arusu.printFellow()

//classは参照型
//Fellowのインスタンスである変数arusuを、変数rotoに代入して再度インスタンス化
var roto = arusu

//プロパティの値を変更
roto.name = "ロト"

//rotoのメソッドを実行
roto.printFellow()

//arusuのメソッドを実行
arusu.printFellow()

//実行結果
/*
 アルスのステータス
 LV:20
 職業:バトルマスター
 ロトのステータス
 LV:20
 職業:バトルマスター
 ロトのステータス
 LV:20
 職業:バトルマスター
 */

//classの継承
class Monster: Fellow{

    //プロパティ
    var skill:String

    //イニシャライザ
    init(name: String, level: Int, skill: String){
        self.skill = skill
        //スーパークラスのイニシャライザを流用
        super.init(name: name, level: level, profession: nil)
    }
    
    //スーパークラスのメソッドをオーバーライド
    override func printFellow() {
        print("\(name)のステータス")
        print("LV:\(level)")
        print("特技:\(skill)")
    }
}

//インスタンス化してメソッド実行
var suraimu = Monster(name: "スライム", level: 18, skill: "メラ")
suraimu.printFellow()

//実行結果
/*
スライムのステータス
LV:18
特技:メラ
*/

以上 class についてでした。

次回は 列挙型 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】switch文

switch文とは、変数や定数の値によって処理を分岐したい場合に使う制御構文です。
基本的にはif文で同じ処理を実装する事ができますが使いわけとしては、
複雑な条件分岐であればif文の方が適していますが、単純な条件や分岐が多い場合はswitch文が適しています。

前回は guard文 について書きました。

今回は switch文 について書いていこうと思います。

switch文とは

switch文 とは、変数や定数の値によって処理を分岐したい場合に使う制御構文です。
基本的には if文 で同じ処理を実装する事ができますが使いわけとしては、
複雑な条件分岐であれば if文 の方が適していますが、単純な条件や分岐が多い場合は switch文 が適しています。

switch文の書式

 switch 値または式{
 case 値1: 処理1
 case 値2: 処理2
 .
 .
 .
 default: デフォルトの処理
 }

switch キーワードを書いた後に値、または式を書きます。
{ } の中に case キーワードを書いて、値 1 だった場合は処理 1 、値 2 だった場合は処理 2 のように分岐を必要な分、箇条書きします。
値と処理は : で区切ります。
default には、どの case にも当てはまらない場合に実行される処理を書きます。

switch文を使った例

let hp = [50, 75, 100, 125, 150]
let result = Int.random(in: 0 ..< hp.count) //0~4
func strategy(HP: Int){
    switch HP{
    case 50:
        print("めいれいさせろ")
    case 75:
        print("いのちだいじに")
    case 100:
        print("いろいろやろうぜ")
    case 125:
        print("バッチリがんばれ")
    case 150:
        print("ガンガンいこうぜ")
    default:
        print("パルプンテ")
    }
}
strategy(HP: hp[result])

配列hp を定義して、Int型 の値( 要素 )を複数代入します。
定数result には 0 ~ 4 の整数がランダムに代入されるようにして、インデックス番号として使います。
この 定数resulthp[ result ] として、 配列hp の 0 ~ 4 番目の中からランダムで要素にアクセスします。
ランダムに選択された要素の値によって処理を分岐して、値と一致した case があれば、その case の処理を実行します。
値がどの case にも当てはまらない場合は、default の処理が実行されます。
関数strategy の引数に hp[ result ] を指定して実行すると、ランダムに選択された値によって switch文 で処理を分岐します。

switchコードimg

範囲演算子を使った switch文 の例

switch文case に範囲演算子を使うことで、値がその範囲に含まれているかどうか判定することができます。

let hp = [50, 75, 100, 125, 150]
let result = Int.random(in: 0 ..< hp.count) //0~4
print(result)
func strategy(HP: Int){
    switch HP{
    case 0...50: //0~50
        print("めいれいさせろ")
    case 51...75: //51~75
        print("いのちだいじに")
    case 76...100: //76~100
        print("いろいろやろうぜ")
    case 101...125: //101~125
        print("バッチリがんばれ")
    case 126...150: //126~150
        print("ガンガンいこうぜ")
    default: 
        print("パルプンテ")
    }
}
strategy(HP: hp[result])

case に範囲演算子を使って、値がどの case の範囲に当てはまるか判定して、当てはまった case の処理を実行します。

範囲演算子 についてはこちらの記事で紹介しています。

switchコードimg2

上の例では 関数strategy の引数を Int型 で指定していますが、FloatDouble型 を指定すると、FloatDouble型 の値を判定することもできます。

let hp:[Float] = [50.0, 75.0, 100.0, 125.0, 150.0]
let result = Int.random(in: 0 ..< hp.count) //0~4
print(result)
print(hp[result])

func strategy(HP: Float){
    switch HP{
    case 0.0...50.0: //0.0~50.0
        print("めいれいさせろ")
    case 51.0...75.0: //51.0~75.0
        print("いのちだいじに")
    case 76.0...100.0: //76.0~100.0
        print("いろいろやろうぜ")
    case 101.0...125.0: //101.0~125.0
        print("バッチリがんばれ")
    case 126.0...150.0: //126.0~150.0
        print("ガンガンいこうぜ")
    default:
        print("パルプンテ")
    }
}

strategy(HP: hp[result])
switchコードimg3

これと同じ処理は if文 でも書くことができますが、else if をいくつも重ねて書くことになると思います。 条件や分岐の数によっては読みづらいコードになることもあると思うので、こういった単純な条件で分岐が多い場合などは switch文 で書いた方が可読性は高いかもしれません。
以上 switch文 についてでした。

次回は class について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】guard文とguard-let文

guard文とは条件を満たさない場合の処理を書く構文で、コードを安全にするために使います。
if-else文でも同様のことはできますが、guard文は条件を満たさない場合のみの処理を書くことができます。
安全にメソッドの処理を途中で終了(return)したいとき、ループ処理の中断(break)したいときなどに使います。

前回は break文 と continue文 について書きました。

今回は guard文 について書いていこうと思います。

guard文

guard文 とは

guard文 とは条件を満たさない場合の処理を書く構文で、コードを安全にするために使います。
if-else文 でも同様のことはできますが、guard文 は条件を満たさない場合のみの処理を書くことができます。
安全にメソッドの処理を途中で終了( return )したいとき、ループ処理を中断( break )したいときなどに使います。

guard文 の書式

 guard 条件 else{
     処理
     return または break
 }

guard文 は基本的に関数内や制御構文内に書きます。
guard キーワードの後に条件を書いて、その後に else キーワードをつけます。
{ } の中に処理を書きますが、処理は条件が false だった場合に実行されます。
関数や制御構文のスコープを抜けるために return または break などが必要です。

guard文 を使った例

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

配列slot に 3 桁の整数を要素として複数代入しています。
配列slot の要素の中からランダムに要素を取り出して、定数stop に代入します。

関数jackpot を定義し Int型 の引数を取って、引数の値によって処理を分岐します。
guard文 の条件は 引数num の値が 777 ではなかったらguard文 の処理が実行されて、return ( 関数から抜ける )します。これを 早期リターン と言います。
引数num の値が 777 だったらguard文 の条件から外れるので、return されずに次の print( ) が実行されアンラップした値を出力します。

randomElement( ) メソッドはコレクション型の要素をランダムに返すメソッドです。返ってくる値は Optional型 なのでアンラップして 定数stop に代入しています。

関数jackpot の引数に 定数stop を指定して実行してみます。

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

//実行結果
/*
145 ハズレ:関数から抜けます。
*/

ランダムに取り出された要素が 145 でした。
条件が false になるので、guard文 の処理を実行して return されます。
return されている( 関数から抜けている )ので、下の print( ) は実行されません。

guardコードimg

次は 777 だった場合の実行結果を見てみます。

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

//実行結果
/*
777 大当たり!
*/

777 だった場合は、guard文 の条件から外れているので return されず下の print( ) が実行されます。

guardコードimg2

guard-let文

guard-let文 とは

guard-let文 とはオプショナルバインディングの 1 つで、Optional型 をアンラップするときに使います。
if-let文 でもアンラップすることができますが、if-let文スコープ内でのみアンラップした定数を使うことができるのに対し、guard-let文スコープの外でも定数を使うことができます。

guard-let文 の書式

guard let 定数名 = Optional型の値 else{
     アンラップに失敗した場合の処理
     return または break
 }

guard-let文guard文 同様、基本的に関数内や制御構文内に書きます。
guard let キーワードの後に定数名を書いて、アンラップしたい Optional型の値 の後に else キーワードを付けます。
{ } の中は、if-let文 と違い、Optional型の値 をアンラップ失敗した場合の処理を書きます。
アンラップ失敗したときの処理なので、スコープを抜けるために return または break などが必要です。

guard-let文 を使った例

let aHalf = ["1", nil, "2", nil, "3", nil]
print(type(of: aHalf)) //Array<Optional<String>>
let stop = Int.random(in: 0 ..< aHalf.count)

func judgement(){
    guard let unwrap = aHalf[stop] else{
        print("nil 関数から抜けます。") //nilだった場合
        return
    }
    print("\(unwrap)") //nilでなければアンラップして出力

}

judgement()

配列aHalfString型 の要素と、nil を 3 つずつ代入しています。
この配列は nil も代入されているので、Optional<String> 型 の配列になります。
random(in:) メソッドを使って、0 ~ 9 の中からランダムに返された値を 定数stop に代入します。
この 定数stop の値をインデックス番号として使います。

関数judgement を定義し、処理で guard-let文 を使って 配列aHalf の要素をアンラップしていきます。
ランダムに選択された aHalf[stop] の値が、アンラップ失敗( 値が nil )であれば、“nil 関数から抜けます。” と出力し return で関数を抜けます。
aHalf[stop] の値がアンラップ成功であれば、guard-let文 の条件から外れるので、return されずに 次の print( ) が実行されて、アンラップされた値を出力します。

if-else文 ではスコープ内に定義した定数はスコープ内でしか使えませんでしたが、guard-let文スコープ外でもアンラップした定数が使う事ができます。

random(in:) は、指定された範囲内でランダムな値を返すメソッドです。
Int.random(in: 0 ..< aHalf.count) とすると、0 から 配列aHalf の要素数 6 ( 6 は含まない ) となるので 0 ~ 5 の中からランダムな整数が返されます。

guardコードimg3

以上 guard文 についてでした。

次回は switch文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】break文とcontinue文

break文 とは、for文 や switch文 などのループ処理を中断する事ができる構文です。
基本的には if文 などと併用して条件を付けて使います。
ループ中に、もし指定した条件を満たしたらループを中断して、次の処理に移りたい時などに使います。

前回は 集合型 について書きました。

今回は break文 と continue文 について書いていこうと思います。

break文

break文 とは

break文 とは、for文switch文 などのループ処理を中断する事ができる構文です。
基本的には if文 などと併用して条件を付けて使います。
ループ中に、もし指定した条件を満たしたらループを中断して、次の処理に移りたい時などに使います。

for文 についてはこちらの記事で紹介しています。 

if文 についてはこちらの記事で紹介しています。

break文 の使い方

使い方は break と書くだけです。

break

break文 を使った例

let slot = [145, 214, 405, 315, 777, 343]

for i in 0 ..< 20 {
    let stop = slot.randomElement()
    print("\(i)回転目 : \(stop ?? 123)")
    
    if stop == 777 {
        print("大当たり!")
        break
    }
}

//実行結果
/*
0回転目 : 315
1回転目 : 315
2回転目 : 343
3回転目 : 315
4回転目 : 145
5回転目 : 145
6回転目 : 315
7回転目 : 214
8回転目 : 405
9回転目 : 777
大当たり!
*/

配列slot に 3 桁の整数を要素として複数代入しています。
for文 は最大 20 回ループするようにしました。
処理は 配列slot の要素をランダムに取り出して、取り出した値を 定数stop に代入します。
print( ) で何回目のループなのかと 定数stop に代入された値を出力します。
if文 で、もし 定数stop に代入された値が 777 だったら、print( )”大当たり!” と出力して、break で中断します。
for文 は 20 回としていますが、20 回ループする前に 777 が選択されたら途中でも for文 を中断します。
また、777 が選択されなければ 20 回ループして for文 を中断します。

randomElement( ) メソッドはコレクション型の要素をランダムに返すメソッドです。返ってくる値は Optional型 なのでアンラップして print( ) で出力しています。

Optional型 についてはこちらの記事で紹介しています。

break-continueコードimg

continue文

continue文とは

continue文 とは、continue と書いた後の処理をスキップする事ができる構文です。
break文 はループ自体を中断して次の処理に移行しますが、continue文 は次回のループ処理に移行する。という違いがあります。
break文 同様 continue文 も条件を付けて使う場合が多いので、if文 などと併用することが多いと思います。

continue文 の使い方

使い方は continue と書くだけです。

continue

continue文 を使った例

上の break文 のコード例を流用

let slot = [145, 214, 405, 315, 777, 343]

for i in 0 ..< 20 {
    let stop = slot.randomElement()
    if stop == 777 {
        print("jackpot!")
        continue
    }
    print("\(i)回転目 : \(stop ?? 123)")
}

//実行結果
/*
0回転目 : 214
1回転目 : 405
2回転目 : 405
3回転目 : 315
4回転目 : 214
jackpot!
6回転目 : 214
7回転目 : 315
8回転目 : 405
9回転目 : 315
10回転目 : 343
11回転目 : 343
12回転目 : 343
13回転目 : 405
jackpot!
15回転目 : 145
16回転目 : 405
17回転目 : 343
18回転目 : 145
19回転目 : 343
*/

for文 は 20 回ループ、処理は 配列slot の要素をランダムに取り出して、取り出した値を 定数stop に代入します。
もし 定数stop に代入された値が 777 だったら、jackpot! と出力し continue して、一番下の print( ) はスキップして次回のループに移行します。
777 以外であれば if文 の条件から外れているので、if文 はスキップされて 一番下の print( ) で何回目のループなのかと 定数stop に代入された値を出力します。
つまり、777 だった時のみ continue して次回のループに、それ以外は continue されずに回転数と値を出力する、という流れになります。

break文break と書かれた後 for文 自体を中断してループを抜けてしまいますが、
continue文continue と書かれた後の処理をスキップするだけなので、for文 は指定通り20 回ループされています。

break-continueコードimg2

以上 break文 と continue文 についてでした。

次回は guard文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】集合型

集合型とは、配列型や辞書型と同じコレクション型の1つで、複数のデータをまとめて入れておける型です。
書式は配列に似ていますが順序を保証しないこと、同一の値を入れられないことが挙げられます。
複数の値を扱いたいが順序は必要なく同一の値を扱わない場合、集合型同士を演算して要素を比較したい場合などが使い道になると思います。

前回は 辞書型 について書きました。

今回は 集合型 について書いていこうと思います。

集合型とは

集合型 とは、配列型や辞書型と同じコレクション型の 1 つで、複数のデータをまとめて入れておける型です。
書式は配列に似ていますが順序を保証しないこと、同一の値を入れられないことが挙げられます。
複数の値を扱いたいが順序は必要なく同一の値を扱わない場合、集合型同士を演算して要素を比較したい場合などが使い道になると思います。

使う時に注意するポイント

  • 要素の順序が保証されない
  • 要素の値は全て異なる値でなければならない
  • 配列型とは違いインデックス番号でアクセスはできない
  • 代入する要素は全て同じ型でなければならない

集合型の書式

var 変数名:Set<要素の型名> = [値, 値, 値, ...]

変数名の後に : で区切って、Set キーワードを書きます。
その後 < > の中に要素の型名を書いて、[ ] の中に全て同じ型の要素を複数書きます。

型推論を使って宣言する場合の書式

var 変数名:Set = [値, 値, 値, ...]

変数名の後に : で区切って、Set キーワードを書きます。
その後 [ ] の中に全て同じ型の要素を複数書きます。
代入した値によって型が推論されます。

※ どちらの書式も配列のリテラルと同じなので Set キーワードが必要です

空の集合型を宣言したい場合

集合型の宣言だけしておいて、後から要素を入れたい場合などに空のまま初期化する事ができます。

//書式1
var 変数名 = Set<要素の型名>()

//書式2
var 変数名:Set<要素の型名> = []

書式 1 と 2 どちらでも空の集合のまま初期化( インスタンス化 )する事ができます。

集合型を使った例

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]

print(type(of: draque)) //Set<String>
print(draque) //["キメラ", "スライム", "ブラウニー"]

型推論を使った集合型の宣言例

//型推論
var drague:Set = ["スライム", "ブラウニー", "キメラ"]

print(type(of: drague)) //Set<String>
print(drague) //["キメラ", "スライム", "ブラウニー"]

集合型は要素の順序を保持しないので、アクセスする度に順序が変わります。

要素を追加する方法

集合型の要素を追加したい場合は、insert メソッドを使います。

集合型の変数名.insert(追加したい値)

要素を追加する例

2 つの要素を追加してみようと思います。

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]
print(type(of: draque)) //Set<String>
print(draque) //["キメラ", "スライム", "ブラウニー"]

//要素を追加
draque.insert("スライムナイト")
draque.insert("おどるほうせき")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

これで要素が 2 つ追加できました。

既存の値を追加してみる

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]
print(type(of: draque)) //Set<String>
print(draque) //["キメラ", "スライム", "ブラウニー"]

//要素を追加
draque.insert("スライムナイト")
draque.insert("おどるほうせき")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

//既存の値を追加
draque.insert("スライム")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

集合型の要素は全て異なる値でなければならないので、追加した値がすでに含まれていた場合、追加しても結果は変わりません。

要素を削除する方法

集合型の要素を削除したい場合は、remove メソッドを使います。

集合型の変数名.remove(削除したい値)

削除したい値はそもそも含まれていない( nil )可能性があります。
なので削除したい値を一度変数に代入して、あったら削除、無かったら代わりの値を出力するといった書き方にすると安全です。

var 変数名 = 集合型の変数名.remove(削除したい値)
集合型の変数名.remove(変数名 ?? 代わりの値)

または
var 変数名:型名? = 削除したい値
集合型の変数名.remove(変数名 ?? 代わりの値)

変数に入れる削除したい値は、無い( nil )可能性がある値なのでこの変数は Optional型 になります。
?? 演算子を使って削除したい値と同一の値が、集合に含まれていればアンラップして削除、含まれていなければ右辺の代わりの値を出力するようにします。

要素を削除する例

2 つの要素を削除してみようと思います。

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]
print(type(of: draque)) //Set<String>
print(draque) //["キメラ", "スライム", "ブラウニー"]

//要素を追加
draque.insert("スライムナイト")
draque.insert("おどるほうせき")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

//既存の値を追加
draque.insert("スライム")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

//要素を削除
//削除したい値を変数に代入する
var removeMonster = draque.remove("キメラ")
var removeMonster2 = draque.remove("スライム")
draque.remove(removeMonster ?? "nil")
draque.remove(removeMonster2 ?? "nil")
print(draque) //["スライムナイト", "おどるほうせき", "ブラウニー"]

集合から 2 つの要素が減っています。
これで安全に 2 つの要素を削除できました。

含まれていない値を削除してみる

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]
print(type(of: draque)) //Set<String>
print(draque) //["キメラ", "スライム", "ブラウニー"]

//要素を追加
draque.insert("スライムナイト")
draque.insert("おどるほうせき")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

//既存の値を追加
draque.insert("スライム")
print(draque) //["スライム", "ブラウニー", "キメラ", "スライムナイト", "おどるほうせき"]

//要素を削除
//削除したい値を変数に代入する
var removeMonster = draque.remove("キメラ")
var removeMonster2 = draque.remove("スライム")
draque.remove(removeMonster ?? "nil")
draque.remove(removeMonster2 ?? "nil")
print(draque) //["スライムナイト", "おどるほうせき", "ブラウニー"]

//含まれていない値を削除
removeMonster = draque.remove("はぐれメタル")
print(removeMonster ?? "nil") //nil

含まれていない値を削除しようとしているので、“nil” が出力されています。

setコードimg

集合型とよく使うメソッドとプロパティ

.contains メソッド

.contains メソッドは、指定した要素が含まれているかどうかを Bool値 で返すメソッドです。
指定した要素があれば true
指定した要素がなければ false が返ってきます。

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]

//.containsメソッド
print(draque.contains("スライム")) //true
print(draque.contains("メタルスライム")) //false

.count プロパティ

.count プロパティは、要素の数を取得する事ができるプロパティです。

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]

//.countプロパティ
print(draque.count) //3

.isEmpty プロパティ

.isEmpty プロパティは、要素が含まれているかどうかを Bool値 で取得することができるプロパティです。
何か要素が入っていれば false
何も入ってなければ true を取得します。
要素が 1 つでも入っていれば false 、空であれば true になります。

var draque:Set<String> = ["スライム", "ブラウニー", "キメラ"]

//.isEmptyプロパティ
print(draque.isEmpty) //false
setコードimg2

集合型の演算

メソッドを使って集合の演算をしていきます。
書式は、

集合A.メソッド名(集合B)

となります。

和集合

和集合を求めるには、union メソッドを使います。
このメソッドは 2 つの集合の を返します。
集合型の要素( 値 )は全て異なる値でなければならないので、重複する値があった場合はカウントしません。

var monsterA:Set = ["スライム", "ブラウニー", "キメラ"]
var monsterB:Set = ["スライム", "キングスライム", "メタルスライム"]
var monsterC:Set<String> = []

//和集合
monsterC = monsterA.union(monsterB)
print(monsterC) //["ブラウニー", "キングスライム", "メタルスライム", "キメラ", "スライム"]

積集合

積集合を求めるには、intersection メソッドを使います。
このメソッドは 2 つの集合の を返します。
2 つの集合の重複した要素( 値 )を返します。

var monsterA:Set = ["スライム", "ブラウニー", "キメラ"]
var monsterB:Set = ["スライム", "キングスライム", "メタルスライム"]
var monsterC:Set<String> = []

//積集合
monsterC = monsterA.intersection(monsterB)
print(monsterC) //["スライム"]

差集合

差集合を求めるには、subtracting メソッドを使います。
このメソッドは、2 つの集合の を返します。
集合 A から 集合 B の重複した要素( 値 )を除いて返します。

var monsterA:Set = ["スライム", "ブラウニー", "キメラ"]
var monsterB:Set = ["スライム", "キングスライム", "メタルスライム"]
var monsterC:Set<String> = []

//差集合
monsterC = monsterA.subtracting(monsterB)
print(monsterC) //["キメラ", "ブラウニー"]

※ 差集合は、集合 A集合 B を反対にすると結果が変わるので注意。

対象差集合

対象差集合を求めるには、symmetricDifferene メソッドを使います。
このメソッドは、2 つの集合の 対象差 を返します。
2 つの集合の重複した要素( 値 )以外を返します。

var monsterA:Set = ["スライム", "ブラウニー", "キメラ"]
var monsterB:Set = ["スライム", "キングスライム", "メタルスライム"]
var monsterC:Set<String> = []

//対象差集合
monsterC = monsterA.symmetricDifference(monsterB)
print(monsterC) //["キメラ", "ブラウニー", "キングスライム", "メタルスライム"]

この他にも Swift にはいろいろなパターンの演算ができるメソッドが用意されています。

setコードimg3

以上 集合型 についてでした。

次回は break文 と continue文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】辞書型

辞書型とはkeyと値を紐付けたデータを複数代入できる型のことで、アクセスするときは、keyを指定してそのkeyに紐付く値にアクセスします。
辞書型という名前の通り、辞書を引くような使い方をしたい時などに使います。
配列とは違い、インデックス番号は使えません。

前回は while文 について書きました。

今回は 辞書型 について書いていこうと思います。

辞書型とは

辞書型とは key を紐付けたデータを複数代入できる型のことで、アクセスするときは key を指定して、その key に紐付く にアクセスします。
辞書型という名前の通り、辞書を引くような使い方をしたい時などに使います。
配列とは違い、インデックス番号は使えません。

配列型 についてはこちらの記事で紹介しています。

辞書型の書式

//書式1
[keyの型名:値の型名]

//書式2
Dictionary<keyの型名,値の型名>

書式 1
[ ] の中に key の型名を書いて : で区切って値の型名を書きます。
型推論を使って定義することも可能です。

書式 2
Dictionary キーワードを書いて < > の中に key の型名を書いて , で区切って値の型名を書きます。

辞書型を使った例

//例1(書式1)
var geregere:[String:Int]
geregere = ["HP": 200, "MP": 70, "ATTACK": 120]

print(type(of: geregere)) //Dictionary<String, Int>
print(geregere["HP"]) //Optional(200)

//例2(書式2)
var borongo:Dictionary<String,Int>
borongo = ["HP": 180, "MP": 75, "ATTACK": 125]

print(type(of: borongo)) //Dictionary<String, Int>
print(borongo["MP"]) //Optional(75)

//例3(型推論)
var tiroru = ["HP": 190, "MP": 80, "ATTACK": 130]

print(type(of: tiroru)) // //Dictionary<String, Int>
print(tiroru["ATTACK"]) //Optional(130)

例 3 のように型推論を使って書くこともできます。

辞書型はアクセスすると Optional型 で返ってくる

上の例のアクセスした結果を見ると頭に Optional と付いています。
key を指定してアクセスしていますが、あるか分からない key にアクセスする可能性があるためです。
確実にある key だと分かっている場合であれば強制アンラップ ! でアンラップしてもいいですが、そうでない場合の方が多いと思うので、そういう場合は ?? 演算子を使って nil だった場合に代わりに代入する値を指定した方が安全です。

Optional型アンラップ についてはこちらの記事で紹介しています。

辞書型の変数定義〜アンラップしてアクセスする例

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]
print(suraimu["HP"]) //Optional(100)

まだアンラップ前なので、100 の前に Optional が付いています。
これをアンラップしていきます。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]
print(suraimu["HP"]) //Optional(100)

//アンラップしてアクセスする
print(suraimu["HP"] ?? "nil") //100

?? 演算子を使って、アクセスした key “HP”nil だった場合は文字列の “nil” を代入します。
これで出力されている値は 100 になったので、
Optional型 から Int型 にアンラップすることができました。

次は key に無いものを指定して、結果が変わるか見てみます。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]
print(suraimu["HP"]) //Optional(100)

//アンラップしてアクセスする
print(suraimu["HP"] ?? "nil") //100

//keyに無いものを指定してみる
print(suraimu["Defense"] ?? "nil") //nil

key“Defense” と指定して、keynil だった場合は文字列の “nil” を代入します。
結果は “nil” になります。
“Defense” という key は定義していないので( nil なので )、右辺の “nil” が代わりに代入されます。

dictionaryコードimg

要素の順番は保証されない

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//要素の順番は保存されない
for i in suraimu.keys {
    print(i)
}

//実行結果
/*
ATTACk
HP
MP
*/

配列型とは異なり、辞書型は要素を順番で管理しないので参照するたびに順番が変わります。そのためインデックス番号が使えません。

要素の追加と変更方法

追加または変更の書式

変数名、または定数名[key] = 値

要素の追加

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//要素の追加
suraimu["Defense"] = 10
print(suraimu) //["HP": 100, "ATTACK": 50, "Defense": 10, "MP": 30]

要素の変更

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//要素の追加
suraimu["Defense"] = 10
print(suraimu) //["HP": 100, "ATTACK": 50, "Defense": 10, "MP": 30]

//要素の変更
suraimu["Defense"] = 20
print(suraimu) //["HP": 100, "ATTACK": 50, "Defense": 20, "MP": 30]

追加したい key が既存でなければ追加、key がもし既存のものであれば値の変更になります。

要素の削除

removeValue(forKey:) メソッド

辞書型の要素を削除したいときは、removeValue(forKey:) メソッド を使います。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//要素の追加
suraimu["Defense"] = 10
print(suraimu) //["HP": 100, "ATTACK": 50, "Defense": 10, "MP": 30]

//要素の変更
suraimu["Defense"] = 20
print(suraimu) //["HP": 100, "ATTACK": 50, "Defense": 20, "MP": 30]

//要素の削除
//removeValue(forKey:)メソッド
suraimu.removeValue(forKey: "Defense")
print(suraimu) //["HP": 100, "ATTACK": 50, "MP": 30]

removeValue(forKey:) メソッドの引数に削除したい key 指定します。 key が既存であれば削除することができます。

辞書型とよく使うメソッドとプロパティ

.contains メソッド

.contains メソッドは、指定した要素が含まれているかどうかを Bool値 で返すメソッドです。
指定した要素があれば true
指定した要素がなければ false が返ってきます。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//.containsメソッド
print(suraimu.keys.contains("HP")) //true
print(suraimu.keys.contains("Defense")) //false

.keys プロパティ

.keys プロパティは、key の一覧を取得することができるプロパティです。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//.keysプロパティ
print(suraimu.keys) //["HP", "MP", "ATTACK"]

.values プロパティ

.values プロパティは、値の一覧を取得することができるプロパティです。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//.valuesプロパティ
print(suraimu.values) //[100, 30, 50]

.count プロパティ

.count プロパティは、要素の数を取得することができるプロパティです。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//.countプロパティ
print(suraimu.count) //3

.isEmpty プロパティ

.isEmpty プロパティは、要素が含まれているかどうかを Bool値 で取得することができるプロパティです。
何か要素が入っていれば false
何も入ってなければ true を取得します。
要素が 1 つでも入っていれば false 、空であれば true になります。

var suraimu:[String:Int]
suraimu = ["HP": 100, "MP": 30, "ATTACK": 50]

//.isEmptyプロパティ
print(suraimu.isEmpty) //false
dictionaryコードimg2

辞書型 はコレクション型の 1 つで、配列型 もその 1 つです。
この 2 つの他に 集合型( Set ) というコレクション型もあります。
以上 辞書型 についてでした。

次回は 集合型 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】while文

while文とはループ処理をすることができる構文です。
前の記事で書いたfor文もループ処理をすることができる構文ですが、
for文はループさせる回数を指定するのに対し、while文は条件から外れるまでループさせる構文で、条件式がtrueの間はループさせ続けることができます。
何回繰り返すか未定のときなどに使います。

前回は Optional型 について書きました。

今回は while文 について書いていこうと思います。

while文とは

while文 とはループ処理をすることができる構文です。
前の記事で書いた for文 もループ処理をすることができる構文ですが、
for文 はループさせる回数を指定するのに対し、while文 は条件から外れるまでループさせる構文で、条件式が true の間はループさせ続けることができます。
何回繰り返すか未定のときなどに使います。

for文 についてはこちらの記事で紹介しています。

while文の書式

 while 条件式(Bool値) {
     処理
 }

while文 は頭に while キーワードを付けます。
条件式( Bool値 )のところには、論理演算子や比較演算子などを使って条件式を書いて { } の中にループさせたい処理を書きます。
条件式が true の間はループし続けるので、処理のところにループを抜ける処理を書くか、条件式にループが終わるように指定しないと無限にループしてしまうので注意が必要です。

論理演算子についてはこちらの記事で紹介しています。

比較演算子についてはこちらの記事で紹介しています。

while文を使った例

0 ~ 9 回目まで同じ処理をループさせる例

var a = 0
while a < 10 {
    print(a)
    a += 1
}

//実行結果
/*
0
1
2
3
4
5
6
7
8
9
*/

0 を代入した 変数 a を準備して、a の値が 10 より小さければ( 10 は含まない ) a の値を print して、a をインクリメントします。
a の値が 10 より大きくなったら( 10 を含む ) while文 を抜けるようにしました。
0 ~ 9 回目までは true なので 9 回目まではループされますが、10 回目は条件式から外れてfalse となるので、while文を抜けます。

※ ループから抜けるように条件式や処理を書かないと無限にループしてしまいます。

同じ処理を for文 で書いてみる

上の while文 と同じ処理を for文 で書くと、

for i in 0 ..< 10 {
    print(i)
}

//実行結果
/*
0
1
2
3
4
5
6
7
8
9
*/

結果は同じですが、while文 は条件式が true の間はループし続けるのに対し、for文 は回数( 範囲 )を指定してループさせるというところに違いがあります。

whileコードimg

repeat-while文

while文との違い

while文 は条件式を先に判定してから true であれば false になるまで処理がループされますが、
repeat-while文 は、先に処理を実行した後に、条件式を判定する。という違いがあります。
後に条件式が判定されるので、判定が true だろうが false だろうが最低でも 1 回は処理が実行されます。

repeat-while文の書式

 repeat {
     処理
 } while 条件式(Bool値)

repeat-while文 は頭に repeat キーワードを付けます。
{ } の中に処理を書いて、閉じかっこ } の後に while キーワードを書いて条件式を書きます。
先に処理、その後条件式の判定なので最低でも 1 回は処理が実行されます。

repeat-while文を使った例

条件式が true だった場合の例

var t = 0
repeat {
    print(t)
    t += 1
} while t < 10

//実行結果
/*
0
1
2
3
4
5
6
7
9
*/

repeat文 の処理が実行された後に、while文の条件式が判定されます。
条件式が false になるまでループします。

条件式が false だった場合の例

var f = 0
repeat {
    print(f)
    f += 1
} while f > 10

//実行結果
/*
0
*/

これは while文 の条件式が false なのでループはしませんが、条件式の判定よりも先に repeat文 の処理が実行されます。
その後に、条件式の判定で false となりrepeat-while文 から抜けます。
なので処理は最初の 1 回目だけ実行されてループから抜けます。

whileコードimg2

ループ処理といえば for文 を使うことが多いと思いますが、ループ処理したいけど何回ループさせるか未定の場合は while文 が適していると思います。
以上 while文 についてでした。

次回は 辞書型 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】Optional型

Optional型( オプショナル型 )とは、nilの可能性があるときに使う型です。
Swiftでは値が無い状態のことを、nilと言います。
例えば、必須項目や非必須項目をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型を使います。

前回は タプル について書きました。

今回は Optional型 について書いていこうと思います。

Optional型とは

Optional型( オプショナル型 )とは、nil の可能性があるときに使う型です。
Swift では 値が無い 状態のことを、nil と言います。
逆に言うと、Optional型 で宣言している場合のみ nil を代入する事ができます。
例えば、必須項目 や 非必須項目 をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型 を使います。

Optional型の書式

//書式1
型名? 

//書式2
Optional<型名>

型名を書いた後に ? を付けます。( 書式 1 )
または
Optional と書いた後 < > の中に型名を書きます。( 書式 2 )

Optional型 の書き方

//例1
var a:Int?
print(type(of: a)) //Optional<Int>

//例2
var b:Optional<Int>
print(type(of: b)) //Optional<Int>

変数 abOptional型 で宣言しています。
この 2 つの変数の型を見てみると、Optional<Int> となっています。これは Optional Int型 という型で、ただの Int型とは別の型になります。なので原則同じ型でなければ演算できないので、Optional Int型Int型 は演算できません。
Optional型 の値を使いたい時は、アンラップ という処理が必要になります。

Int型 に限らず、他の型でも頭に Optional と付いている型の値を使いたい時はアンラップが必要です。

※ 例 1 は初期値を入れずに宣言するとデフォルトで nil が代入されますが、
例 2 は初期値を入れずに宣言しても nil が代入されません。例 2 はどこかで値を代入しないと使うことができないので注意。

//例1
var a:Int?
print(type(of: a)) //Optional<Int>
print(a) //nil

//例2
var b:Optional<Int> = nil //初期値としてnilを代入
print(type(of: b)) //Optional<Int>
print(b) //nil

Optional型を使った例

必須項目と非必須項目がある場合の例

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String? //非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

必須項目と非必須項目に使う変数・定数を宣言して、必須項目にだけ値を代入し全ての変数・定数にアクセスしている例です。
必須項目には値を代入しているのでこれまで通りアクセスできて値もきちんと反映されています。
次に非必須項目ですが、 値を代入していません。
Optional型 で宣言しているので値の代入がされてなくてもデフォルトで初期値に nil が代入されます。
Optional型nil許す型なので、アクセスしたとき値が無くてもエラーやクラッシュではなく nil が返ってきます。
もしこれを Optional型 ではなく String型 などで宣言していれば、値が無い( 初期値が無い )のにアクセスしてしまいエラーやクラッシュする原因になります。

次は非必須項目に値を入れてアクセスしてみます。

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String?//非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

非必須項目にも値を代入して再度アクセスしました。
値を入れずにアクセスした時は nil が代入されていましたが、値を入れてアクセスすると nil ではなく、
phone には Optional(“123-4567-8910”)
birthday には Optional(“20XX/XX/XX”)
が代入されています。
String型 の値を代入したのに、この 2 つの変数は Optional String型 で宣言されているので、頭に Optional が付いた状態になっています。
この状態では print関数 で中の値は確認できますが、実際に値を使うことはできません。
使おうとすると、アンラップする必要があります!とエラーが出ます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//Optional String型の変数をString型の変数に入れようとするとエラー
var phoneNumber:String = phone //エラー
optionalコードimg

アンラップ

アンラップとは、ラップ( 包装 )の反対語でラップを外すこと。と表現されている事が多いと思います。
Optional型 はラップ( 包装 )された状態で、このラップを外すことをアンラップと言います。

Optional型の値を 非Optional型 の値と同様に扱いたいときは、アンラップ( unwrap )という処理をしなければ使うことができません。

アンラップする方法

  • ?? 演算子( nil合体演算子 )
  • ! 演算子( 強制アンラップ )
  • if-let文 ( オプショナルバインディング )

この他にも guard-let などいくつかアンラップする方法がありますが、今回は上記の 3 種類を使ってアンラップしていきたいと思います。

?? 演算子 ( nil合体演算子 )

?? 演算子を使ったアンラップの書式

Optional型の値 ?? アンラップ失敗または、nilだった場合に入る値(デフォルト値)

左辺の Optional型の値 がアンラップ可能であれば アンラップした値 を代入し、
アンラップ失敗( 値が nil ) であれば右辺の デフォルト値 を代入する演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//??演算子(nil合体演算子)
var phoneNumber:String = phone ?? "アンラップ失敗"

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone がアンラップ可能であれば アンラップした値変数 phoneNumber に代入します。
もしアンラップ失敗や、そもそも値が nil であれば右辺の “アンラップ失敗” という文字列を phoneNumber に代入します。

アンラップ前の phone の値には Optional と頭に付いていましたが、
アンラップ後の値を見るとOptional が取れて普通の String型 にアンラップされています。これで String型 の値として扱うことができるようになりました。
アンラップが成功した場合と失敗した場合の両方の処理を書くので、安全にアンラップする方法だと思います。

! 演算子 ( 強制アンラップ )

! 演算子を使ったアンラップの書式

Optional型の値!

Optional型の値 の後に ! を書くことで強制的にアンラップする事ができる演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//!演算子(強制アンラップ)
var phoneNumber:String = phone!

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone の後に ! を書いて強制的にアンラップした値を 変数 phoneNumber に代入します。
アンラップ後のアクセスした結果を見るとアンラップされているのが分かります。

※ ただし ! 演算子( 強制アンラップ )は、 Optional型の値nil だった場合に強制アンラップするとプログラムがクラッシュしてしまいます。
値が nil なのに( アンラップの対象が無いのに )強制アンラップしているためです。
なので nil の可能性がある場合、値が入っているか分からない場合は使えません。
確実に値が入っていると分かっている場合に使います。

if-let文 ( オプショナルバインディング )

if-let文 を使ったアンラップの書式

if let 定数名 = Optional型の値 {
    アンラップに成功したらこの中の処理が実行
} else {
    アンラップ失敗したらこの中の処理が実行される
    else文を書いていない場合、このif-let文はスキップ
}

Optional型の値 がアンラップ可能であれば、アンラップした値を定数に代入し、{ } の中の処理が実行されます。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。

else文 を書かなかった場合、アンラップ失敗( 値が nil )であれば if-let文 はスキップされます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//if-let文(オプショナルバインディング)
if let phoneNumber = phone {
    print(phoneNumber) //電話番号 123-4567-8910
    print(type(of: phoneNumber)) //String
} else {
    print("アンラップ失敗")
}

Optional型変数 phone がアンラップ可能であれば アンラップした値定数phoneNumber に代入し、{ } の中の処理を実行します。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。
このアンラップ方法も アンラップが成功した場合と失敗した場合の両方の処理を書けるので、nil の値をアンラップしようとしてクラッシュするなどのエラー回避につながります。

エラーやクラッシュを回避するためにも、できるだけ強制アンラップは使わず、?? 演算子や if-let文 でアンラップした方が安全だと思います。

お疲れ様でした。
以上 Optional型 についてでした。

次回は while文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】タプル

タプルとは複数の値を並べて順序をつけたもので、複数の値を 1 つのデータとして扱う事ができるものです。
構造体や class を作るほどではない時や、簡単に型が違う値を扱いたい時などに使います。
アクセスする時は配列同様インデックス番号を使うか、要素にラベルを付けることでラベルでアクセスすることもできます。

前回は 三項演算子 について書きました。

今回は タプル について書いていこうと思います。

タプルとは

タプルとは複数の値を並べて順序をつけたもので、複数の値を 1 つのデータとして扱う事ができるものです。
構造体 や class を作るほどではない時や、簡単に型が違う値を扱いたい時などに使います。
アクセスする時は 配列 同様インデックス番号を使うか、要素にラベルを付けることでラベルでアクセスすることもできます。

配列と違う点

タプル は 配列型 と似ていますが違いとして、配列は例外を除き代入する全ての要素は同じ型でなければならないのに対し、タプルはそれぞれ違う型の要素を複数入れておく事ができます。
タプルは要素を変更することはできますが、要素の追加削除はできません。

配列型 についてはこちらの記事で紹介しています。

タプルの書式

( ) の中に要素をカンマ , で区切って並べます。

(要素, 要素, 要素, ...)

初期値が決まってない場合(後で入れたい場合)は型名のみ宣言する

(型名, 型名, 型名, ...)

タプルの書き方

初期値が決まっている場合の例

//初期値が決まっている場合
var a = (1, 2, 3)

print(a) //(1, 2, 3)
print(type(of: a)) //(Int, Int, Int)

変数 a に タプルで Int型 の要素を 3 つ代入しています。

型名だけ宣言する場合の例

//型名だけ宣言
var b: (Int, String, Double)

変数 b にタプルで型名だけ 3 種類の要素を宣言しています。
この要素に値を入れていきます。

//型名だけ宣言
var b: (Int, String, Double)

//bに要素を代入
b = (1, "fuga", 1.23)

print(b) //(1, "fuga", 1.23)
print(type(of: b)) //(Int, String, Double)

これでタプルの特徴である、型が違う要素を複数代入することが出来ました。

※ 後から要素を代入するときは、宣言した時の型名の順番と同じ順番の型で要素を入れないとエラーになります。

次は要素の変更をしてみます。

//型名だけ宣言
var b: (Int, String, Double)

//bに要素を代入
b = (1, "fuga", 1.23)

print(b) //(1, "fuga", 1.23)
print(type(of: b)) //(Int, String, Double)

//bの要素を変更してみる
b = (2, "hoge", 4.56)

print(b) //(2, "hoge", 4.56)

これでタプルの要素が変更されました。

※ 要素の変更は可能ですが要素の追加削除はできません。

アクセス

インデックス番号でアクセスする

アクセス方法は 、
変数、定数名 . インデックス番号
でアクセスします。

//型名だけ宣言
var b: (Int, String, Double)

//bに要素を代入
b = (1, "fuga", 1.23)

print(b) //(1, "fuga", 1.23)
print(type(of: b)) //(Int, String, Double)

//bの要素を変更してみる
b = (2, "hoge", 4.56)

print(b) //(2, "hoge", 4.56)

//インデックス番号でアクセスする
print(b.0) //2
print(b.1) //hoge
print(b.2) //4.56

インデックス番号 についてはこちらの記事で紹介しています。

tupleコードimg

タプルの要素にラベルを付ける

ラベルを付けるときの書式

(ラベル名: 要素, ラベル名: 要素, ラベル名: 要素, ...)

( ) の中に、ラベル名を書いた後コロン : を書いて要素を代入します。

//タプルの要素にラベルを付ける
var c = (level: 5,
         name: "ゲレゲレ",
         attack: 100)

ラベルでアクセスする

ラベルでアクセスする時は、
変数・定数名 . ラベル名
でアクセスします。

//タプルの要素にラベルを付ける
var c = (level: 5,
         name: "ゲレゲレ",
         attack: 100)

//ラベルでアクセス
print(c.level) //5
print(c.name) //ゲレゲレ
print(c.attack) //100

ラベル名でアクセスできました。
また、ラベルを付けたタプルでも
インデックス番号でアクセスできます。

//タプルの要素にラベルを付ける
var c = (level: 5,
         name: "ゲレゲレ",
         attack: 100)

//ラベルでアクセス
print(c.level) //5
print(c.name) //ゲレゲレ
print(c.attack) //100

//インデックス番号でアクセスする
print(c.0) //5
print(c.1) //ゲレゲレ
print(c.2) //100
tupleコードimg2

型名だけ宣言して後で要素を入れたい場合も、同様にラベルを付けることができます。
以上 タプル についてでした。

次回は Optional型 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】三項演算子

三項演算子とは、オペランド(被演算子)を 3 つ必要とする演算子で、Bool値 ( true , false ) によって代入する値を分岐できる演算子です。
簡単な分岐であれば、1 行のコードで if文 と同じ処理をする事ができます。
if文 を使って分岐するほどではない時に使います。

前回は 構造体 について書きました。

今回は 三項演算子 について書いていこうと思います。

三項演算子とは

三項演算子とは、オペランド(被演算子)を 3 つ必要とする演算子で、Bool値 ( true , false ) によって代入する値を分岐できる演算子です。
簡単な分岐であれば、1 行のコードで if文 と同じ処理をする事ができます。
if文 を使って分岐するほどではない時に使います。

if文 についてはこちらの記事で紹介しています。

三項演算子の書式

条件式 ? trueだった場合の値 : falseだった場合の値

条件式 の後に ? マークを書きます。
その後に、
true だった場合の値 : false だった場合の値
を書きます。

条件式の結果が
true であれば左辺の値
false であれば右辺の値
が代入されます。

三項演算子を使った例

整数の大きさによって処理を分岐する例です。

var hp = 60
var strategy: String

//三項演算子の例
strategy = hp <= 50 ? "いのちだいじに" : "ガンガンいこうぜ"
print(strategy) //ガンガンいこうぜ

変数 hp には 60 が代入されています。
条件式は hp の値が 50 以下( 50 も含まれる)であれば true 、それ以外であれば false となります。
上の例では、hp の値が 50 以上なので false となり、右辺の値が 変数 strategy に代入されます。

比較演算子 についてはこちらの記事で紹介しています。

if文 で書いた場合

上のコードを if文 で書いた場合の例

var hp = 60
var strategy: String

//三項演算子の例
strategy = hp <= 50 ? "いのちだいじに" : "ガンガンいこうぜ"
print(strategy) //ガンガンいこうぜ

//上のコードをif文で書いた場合
if hp <= 50 {
    strategy = "いのちだいじに"
} else {
    strategy = "ガンガンいこうぜ"
}
print(strategy) //ガンガンいこうぜ

三項演算子を使った分岐は print関数 を抜かすと 1 行で書けるのに対し、同じ処理を if文 で書くと 5 行になってしまいます。
このぐらいの単純な分岐であれば三項演算子を使った方が可読性が高いかもしれません。

分岐を増やしたい場合

分岐を増やしたい場合の書式

条件式a ? trueだった場合の値a : 条件式b ? trueだった場合の値b : falseだった場合の値

条件式 a の後に ? true だった場合の値 a と書きます。
コロン : を書いて 条件式 b を書きます。
その後に ? trueだった場合の値 b : falseだった場合の値
を書きます。

コードが読まれていく順序

最初の 条件式 atrue であれば true だった場合の値 a が代入される。

false であれば次の 条件式 b に進む

条件式 btrue であれば true だった場合の値 b が代入される

false であれば false だった場合の値 が代入される

という流れになります。

三項演算子を使って分岐を増やしたい場合の例

var hp = 0
var strategy: String

//分岐を増やしたい場合の例
strategy = hp == 0 ? "ザオラル!" : hp <= 50 ? "いのちだいじに" : "ガンガンいこうぜ"
print(strategy) //ザオラル!

条件に 変数hp の値が 0 だった場合の処理を追加しました。

if文 で書いた場合

上のコードを if文 で書いた場合の例

var hp = 0
var strategy: String

//分岐を増やしたい場合の例
strategy = hp == 0 ? "ザオラル!" : hp <= 50 ? "いのちだいじに" : "ガンガンいこうぜ"
print(strategy) //ザオラル!

//上のコードをif文で書いた場合の例
if hp == 0 {
    strategy = "ザオラル!"
} else if hp <= 50 {
    strategy = "いのちだいじに"
} else {
    strategy = "ガンガンいこうぜ"
}
print(strategy) //ザオラル!
ternary-operatorコードimg

2 択の単純な分岐であれば三項演算子を使った分岐の方が短いし楽ですが、分岐が増えてくると 1 行とはいえ見づらいと思います。
分岐が増えるかもしれない場合は最初から if文switch文 を使った方がいいかもしれません。
以上 三項演算子 についてでした。

次回は タプル について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/