【プログラミング初心者】Swift練習問題~繰り返し２~ 回答例

以下で投稿した問題の回答例です。

#練習問題
##練習問題１−１
###回答例
```swift
func sum(of values: [Double]) -> Double {
    var sum = 0.0
    for value in values {
        sum += value
    }
    return sum
}
```
###解説
`for`でループを回し全ての値を足し合わせます。

##練習問題１−２
###回答例
```swift
func average(of values: [Double]) -> Double? {
    let sumOfValues = sum(of: values)
    if values.isEmpty {
        return nil
    }
    return sumOfValues / Double(values.count)
}

func sum(of values: [Double]) -> Double {
    var sum = 0.0
    for value in values {
        sum += value
    }
    return sum
}
```
###解説
【練習問題１−１】で作成した関数を使いまわしています。
作った関数はどんどん使いまわしていきましょう。

結果として[合計 / 要素数]とし平均値を返しています。
`values.count`は`Int`型のため`Double`型に変換して計算しています。

`values.isEmpty`では要素数が０かどうか判定します。
`配列.isEmpty`は配列の要素が０の場合trueを返します。
配列が空の場合`values.count`は０となり、分母が０で計算できません。
そのため空のときは計算できないという意味でnilを返しています。

##練習問題２
###回答例
```swift
func max(of values: [Int]) -> Int? {
    if values.isEmpty {
        return nil
    }
    var tmpMax: Int? = nil
    for value in values {
        if let unwrappedMax = tmpMax {
            if value > unwrappedMax {
                tmpMax = value
            }
        } else {
            tmpMax = value
        }
    }
    return tmpMax
}
```
###解説
まず`values`が空の場合は最大値となる数値が存在しないためnilを返します。

`var tmpMax: Int? = nil`として最大値となる値を格納する変数を定義します。
型は`Int?`とオプショナル型で定義しています。
最初は入れるべき値が不明なためnilとするためです。
例えば初期値に0をいれてしまうと`values`の値が全て負の数の場合、最大値として０を返してしまうためそれは不正解です。
この時点では要素数は１つ以上ということが確定しているので`var tmpMax = values[0]`とするのはOKです。
その場合`for`内のアンラップ処理が不要となるのでもう少しすっきりした書き方ができます。

`for`のアンラップ処理で`tmpMax`がnilの場合比較対象がないためvalueをそのまま入れています。
ここで`values[0]`が格納されます。

`if value > unwrappedMax`で`tmpMax`に保持していた値と`value`を比較し大きい方を新しい`tmpMax`として更新します。
配列の値全てを検証し終えた後`tmpMax`に格納されている値が最大値となるので戻り値として返しています。

解説はしませんがシンプルな書き方として別解を載せます。

```swift
func max(of values: [Int]) -> Int? {
    guard var tmpMax = values.first else {
        return nil
    }
    for value in values {
        tmpMax = value > tmpMax ? value : tmpMax
    }
    return tmpMax
}
```

#応用問題
##応用問題１
###回答例
```swift
func twoSums(from values: [Int], target: Int) -> [Int] {
    var currentIndex = 0
    for currentValue in values {
        let def = target - currentValue
        var defIndex = 0

        for defValue in values {
            if def == defValue && currentIndex != defIndex {
                return [currentValue, defValue]
            }
            defIndex += 1
        }

        curentIndex += 1
    }
    
    return []
}
```
###解説
まず`values`に対してループを二重に回し、それぞれ判定しています。
`currentIndex`は外側のループ回数、`defIndex`は内側のループ回数です。

外側のループの`let def = target - currentValue`では`currentValue`にいくつ足せば`target`の値になるか計算します。

さらに内側で`values`に対しループを回し、値の中で`def`と等しい数字があるかを検索します。
`if def == defValue && currentIndex != defIndex`という条件を切っています。
`def == defValue`は条件に合致する値かを判定しています。

`currentIndex != defIndex`では外側の`currentValue`とは異なる要素番号なのかを判定しています。

例えば`values`が[5, 3, 1]、`target`が10だったと仮定します。
これは足して10となる組がないので空配列が返ることを期待します。
ですが`currentIndex != defIndex`の条件がなれば、外側と内側ループがそれぞれ0番目の要素に「5」という値を見つけてしまい[5, 5]という配列を戻り値として返してしまいます。
そのための条件です。

では`currentValue != defValue`としても同じかと思う方もいるかもしれませんが、その場合[5, 3, 1, 5]に対応できなくなるのでNGです。

条件に合致する値の組を見つけた場合は`for`の途中で戻り値を返し処理を終了します。
`for`の検索が終わっても戻り値を返していない場合は見つからなかったとして最後に空配列を返します。

###別解
この処理は配列の要素数の２乗分ループが回りますが無駄な処理が多いです。
例えば`currentIndex`が２のとき、すでに要素番号0, 1の検索は終わっています。
`(currentIndex, defIndex)`として`(0, 2)`、`(1, 2)`では条件を満たせなかったということは`(2, 0)`、`(2, 1)`も条件満たさないことがわかるため、無駄なループです。
また`(currentIndex, defIndex)=(2, 2)`も仕様として許可していないので`defIndex`は3以降で検索すればよくなります。
この無駄を省いた処理が以下になります。

```swift
func twoSums(from values: [Int], target: Int) -> [Int] {
    var curentIndex = 0
    for currentValue in values {
        let def = target - currentValue
        for defIndex in (curentIndex + 1)..<values.count {
            let defValue = values[defIndex]
            if def == defValue {
                return [currentValue, defValue]
            }
        }
        curentIndex += 1
    }
    
    return []
}
```

##応用問題２
###回答例
```swift
func getPrimeNumbers(limit: Int) -> [Int] {
    guard limit > 1 else {
        return []
    }
    
    var primeNumbers: [Int] = []
    
    for currentNumber in 2...limit {
        if checkPrimeNumber(number: currentNumber) {
            primeNumbers.append(currentNumber)
        }
    }
    
    return primeNumbers
}

func checkPrimeNumber(number: Int) -> Bool {
    guard number > 1 else {
        return false
    }
    
    for currentNumber in 2..<number {
        if number % currentNumber == 0 {
            return false
        }
    }
    
    return true
}
```
###解説
素数かどうかを判定する関数として`checkPrimeNumber`を作成しています。
`checkPrimeNumber`を作成せず全ての処理を`getPrimeNumbers`に書いていても問題ありません。

####checkPrimeNumber
与えた数字が素数かどうかを判定し、true/falseを戻り値として返す関数として定義しています。
`guard number > 1 else`で１より小さい数字は素数ではないので先に処理してガードしています。

`for`で２〜number-1まで総当りで判定します。
どれかひとつでも割り切れたら素数ではないので、割り切れた時点でfalseを返します。
最後までループが回ると割り切れなかったということなので、numberは素数と判定でき、trueを返します。

####getPrimeNumbers
`limit`が１より小さい場合、素数は存在しないためガードし、先に空配列を返します。

`guard`文で値を２以上に絞ったためループの条件を2〜limitとし、２から順に総当りで判定します。
`checkPrimeNumber`で素数かどうかを判定し、素数の場合は配列に追加します。

最後までループを回しきれば`primeNumbers`に全ての素数が格納されるのでこれを戻り値とします。