Swiftらしいコーディングを学ぶ - map, filter, reduceについて

# 🎉 はじめに
Swiftを利用してiOSアプリを開発している初心者たちは、配列を扱うときにやはり `for` 文を使ってしまうのではないでしょうか。（少なくとも僕はそうでした）

僕の最初の記事で、平成最後のアドベントカレンダーの記事の一つとして、いくつかSwiftの**高階関数**を紹介したいと思います。

# 💻 この記事の環境
- Xcode 10.1
- Swift 4.2

# 🧐 その前に
僕はC言語を最初のプログラミング言語として習ったので、Swiftを勉強し始めた時はいつもこんなコードを書いてしまいました。

例えば、`[1, 2, 3, 4, 5, 6, 7, 8]` という配列が与えられ、その中の要素を全て二乗したい時は考えましょう。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var squareNumbers: [Int] = []
for number in numbers {
  squareNumbers.append(number * number)
}
print(squareNumbers)
// 出力: [1, 4, 9, 16, 25, 36, 49, 64]
```

このように、二乗された要素を新しい配列にまとめることはできたが、`for` 文で処理を行うと `squareNumbers` という配列を初期化する手間が掛かると思わないでしょうか。よりSwiftらしい書き方があるはず、と僕はそう考えていました。

そこで、高階関数(`map`, `compactMap`, `filter`, `reduce`)の出番です。

# 💪🏻 高階関数
> 高階関数（こうかいかんすう、英: higher-order function）とは、第一級関数をサポートしているプログラミング言語において、関数（手続き）を引数にしたり、あるいは関数（手続き）を戻り値とするような関数のことである。
[wikipedia参照](https://ja.wikipedia.org/wiki/%E9%AB%98%E9%9A%8E%E9%96%A2%E6%95%B0)

**高階関数**は、主に関数型言語やラムダ計算において使用される関数で、SwiftのFunctionalな書き方を理解するためにも、より簡潔なコード書くためにも、**高階関数**を学んでおきたい関数だと思います。

## 💡 `map()`

### 利用場面
- `map` は**配列の全要素に処理を適用し、その処理をを施された配列**を使いたい場合に使用します。

### 使い方
この前に例として上げた「配列の全要素を二乗したい」コードを振り返ると、やはり長いですね。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var squareNumbers: [Int] = []
for number in numbers {
  squareNumbers.append(number * number)
}
print(squareNumbers)
// 出力: [1, 4, 9, 16, 25, 36, 49, 64]
```

これを `map` を使うと以下のように書き換えます。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var squareNumbers = numbers.map({ $0 * $0 })
print(squareNumbers)
// 出力: [1, 4, 9, 16, 25, 36, 49, 64]
```

どうでしょうか、かなり簡潔に書けたのではないでしょうか。

## 💡 `filter()`

### 利用場面
- `filter` は**配列の全要素に処理を適用し、条件にマッチする要素のみが含まれる配列**を使いたい場合に使用します。

### 使い方
`map` と同じく、`Int` 型の配列を例として説明します。例えば、ある配列の中の偶数だけ取得したい時にどうすればいいのでしょうか。

まずは、いつもの `for` 文の書き方を見ていきましょう。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var evenNumbers: [Int] = []
for number in numbers {
  if number % 2 == 0 {
    evenNumbers.append(number)
  }
}
print(evenNumbers)
// 出力: [2, 4, 6, 8]
```

できないわけではないですが、やはりSwiftらしく書きたいですね。`if` 文をなくすとしたらどうなりますか。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var evenNumbers: [Int] = []
for number in numbers where number % 2 == 0 {
  evenNumbers.append(number)
}
print(evenNumbers)
// 出力: [2, 4, 6, 8]
```

少しは簡潔に書けましたね、「これも一行で解決できるのでは」と思わないでしょうか。
もちろん、`filter` を使えばできます。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var evenNumbers = numbers.filter({ $0 % 2 == 0 })
print(evenNumbers)
// 出力: [2, 4, 6, 8]
```

これで `map` と同じように一行で簡潔に書けます。

## 💡 `reduce()`

### 利用場面
- `filter` は**配列の全要素を一つの要素にまとめる**場合に使用します。

### 使い方
「配列の全要素を足す」という時を考えましょう。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var sum: Int = 0
for number in numbers {
  sum += number
}
print(sum)
// 出力: 36
```

またこのような長いコードを見ると、「`for` 文はもう使いたくない」と考えるのではないでしょうか。早速、`reduce` を使って書き換えてみると。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var result = numbers.reduce(0, +)
print(result)
// 出力: 36
```

これだけで完了です。一つ注意してほしいのは `reduce` の第一引数は「初期値」で、第二引数は「演算子」です。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var result1 = numbers.reduce(0, +)
var result2 = numbers.reduce(1, +)
print(result1, result2)
// 出力: 36　37
```

上の例で書いてある通り、初期値を変えると、結果も変えてしまうので、これを気をつけて使いましょう。

```swift
let numbers: [Int] = [1, 2, 3]
var result = numbers.reduce(1, *)
print(result)
// 出力: 6
```

さらに、このように普段よく使う演算子 `+`, `-`, `*` を入れ替えてもオーケーです。

## 🛠 メソッドチェーン
ここまで、`map`, `filter`, `reduce` について簡単な使い方を紹介しましたが、これらの**高階関数**をまとめて使う例も一緒に紹介しようと思います。

例えば、`[1, 2, 3, 4, 5, 6, 7, 8]` という配列が与えられ、この配列の全要素をまず二乗にして、そのうち `20` より小さい要素の和を求めたい時はどうすればいいでしょうか。

二乗した配列は `[1, 4, 9, 16, 25, 36, 49, 64]` なので、そのうち `20` より小さい要素は `[1, 4, 9, 16]` で、全部足すと `30` になるはずです。

ここで、`for` 文を使った例は省略し、**メソッドチェーン**を利用して今までの**高階関数**を一気に使うと。

```swift
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8]
var result = numbers.map({ $0 * $0 }).filter({ $0 < 20 }).reduce(0, +)
print(result)
// 出力: 30
```

やはり一行で簡潔に結果を正しく計算できました。

# 🤩 まとめ
以上、**高階関数**についてよく使うものについて、簡単な使い方の紹介でした。

いかがでしょうか、これらの関数を理解するには時間がかかるかもしれないが、使い方を慣れれば開発の効率が一気に上がり、コードもより簡潔に書けると思いますので、ぜひ使ってみてください。

他にも、`compactMap`, `sort`, 'firstIndex' などたくさんの関数があるので、興味がある方はいろいろ調べてもらえばと思います。

最後に、ここまで読んでいただいてありがとうございました。
僕は留学生なので、日本語が変だと思われるところがあるかもしれないが、この記事でSwiftをよりSwiftらし書けるようになったら嬉しいです。