繰り返し構文が一段の場合、for式ではなくListのmap関数を使おう

この記事は[東京理科大学 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/tokyo_university_of_science) 12/17の記事です。

はじめまして。
5年ほど前に工学部建築学科(A科)を卒業し、IT業界でScalaエンジニアをやっています。
アウトプット量を増やすべく記事を書いています。

※2017/12/31 追記しました。

## 背景
`Scala`でループ処理や入れ子処理を書くときは、なんとなくfor式をよく使うのですが
職場の先輩より

```test
for式はmapとflatMapのシンタックスシュガーなのでコンパイル速度とかに影響してきます。
ループが一段であればmapを使用しましょう。
```
と、アドバイスを頂きました。

その時はアドバイスが知的すぎて普通に理解できなかったので
今ここでこの言葉の意味をしっかり理解したいと思います。

## 一般的なfor式の使い方(4パターン)
そもそもfor式の一般的な使い方を理解

### 1.繰り返し処理を行いたい
```scala
for (変数名 ← コレクション) {
処理
}
```

```scala
  for(x <- 1 to 3){
    println(x)
  }

(出力結果)
1
2
3
```
また、for式の(変数名 ← コレクション)をジェネレーターと呼び、コレクションから要素を一つ取り出して変数にセットします。
※ コレクションについては、以下の記事に詳しく説明されています。
[Scalaのコレクションとかについて](https://qiita.com/544/items/f36c018d0568156d09cb)
### 2.フィルタリングしつつ繰り返し処理を行いたい

```scala
for (変数名 ← コレクション if 条件) {
処理
}
```

```scala
  for(x <- 1 to 3 if x % 2 == 1 ){
    println(x)
  }

(出力結果)
1
3
```
### 3.繰り返し処理して新しいコレクションを作りたい
```scala
val 変数 = for (変数名 ← コレクション) yield {
処理
}
```
```scala
  val oddVector = for(x <- 1 to 3 if x % 2 == 1 ) yield x
// => Vector(1, 3)
```

### 4.ネストしたデータ構造を繰り返し処理したい

```scala
for { 変数名 ← コレクション
      変数名 ← コレクション }{
 処理
}
```
```scala
  for { x <- 1 to 2
        y <- 4 to 5
  }{
    println(x,y)
  }

(出力結果)
(1,4)
(1,5)
(2,4)
(2,5)
```
以上の4パターンがfor式の一般的な使い方です。

---

ではシンタックスシュガーになる関数を見ていきます。

## mapメソッドについて
`map`とは、コレクションの`List`の関数です。`map`メソッドを使うと`List`の要素を変換して新しいListを作成できます。

```scala
  val list = List(1,2,3)
  val newList = list.map(_.*(2))
// => List(2, 4, 6)
```
## flatMapメソッドについて
`flatMap`も、`map`と同様にコレクションの`List`の関数です。`flatMap`メソッドでは、`List`の要素に対して`map`処理を行い、その結果を連結した`List`を返します。

```scala
  val list = List(1,2,3)
  val newList = list.flatMap {
    e => List(e, e * 5 )
  }
// => List(1, 5, 2, 10, 3, 15)
```

以上が今回指摘された件で抑えておきます。

## 繰り返し構文が一段のfor式の例文

```scala
  //モデル
  case class Company(name: String, carModel: String)
```

```scala
  //コレクション
  val companyList = List(
    Company("TOYOTA", "AQUA"),
    Company("HONDA", "FIT"),
    Company("SUZUKI", "SWIFT"),
    Company("NISSAN", "NOTE"))
```
```scala
  //繰り返し構文で全てのカーモデルを返す
  def getAllCarModel: List[String] = {
    for {
      company <-companyList
    } yield {
      company.carModel
    }
  }

// getAllCarModel
// => List(AQUA, FIT, SWIFT, NOTE)

```
一見繰り返して使いたい時なので良いのではないかなと感じますが、
上記の内容は、`for`式を使わなくても`map`で実現できます。

```scala
  def getAllCarModel: List[String] = {
    companyList.map((company) => company.carModel)
  }

// getAllCarModel
// => List(AQUA, FIT, SWIFT, NOTE)
```
確かに`map`で書いた方が簡潔です。

Scalaで繰り返し構文を実現する上でよく`for`式を使いますが、
繰り返し構文が一段の場合で、`yield`を使い新しい`List`を作っている処理に関しては`map`の方が良さそうですね。(ようやく先輩の言っていたことを理解しました！)

それでは

## 補足
[gakuzzzzさんのスライド](http://gakuzzzz.github.io/slides/for_comprehension_reintoroduction/#31)より
>`Scala`では、`for-yield`で書いた式はコンパイル時に`map/flatMap`を使った式に変換されます。

`for-yield`を使っていると、コンパイル時に`map/flatMap`を使った式に変換されるので、
`map`で最初っから記載できる単純なコードであれば、最初っから`map`で書きましょうということですね。
コンパイル速度が遅いってそういうことなんですね。

----
# 追記(2017-12-31)
速度に言及する内容を書く場合は、**きちんと計測しましょう**という声を頂いたので、[JMHを使ってScalaでかいたコードのパフォーマンスを計測する](https://qiita.com/taketora/items/0cee66b5d5552285f5de)という記事を書いて調べてみました。
※ただコンパイルの速度は調べ方がわからなかったのでコンパイルの速度は調べていません。

サンプルコード

```scala
package sample

import org.openjdk.jmh.annotations.Benchmark

class JMHSample {

  @Benchmark
  def measureMapMeth(): Seq[Int] = {
    mapMeth(10000)
  }

  @Benchmark
  def measureForMeth(): Seq[Int] = {
    forMeth(10000)
  }

  def mapMeth(x: Int): Seq[Int] = {
    1.to(x).map(x => x.*(2))
  }

  def forMeth(x: Int): Seq[Int] = {
    for {
      a <- 1 to x
    } yield {
      a.*(2)
    }
  }
}
```


JMHで確認

```shell

> jmh:run -i 20 -wi 20 -f1 -t 1
・・・
[info] Benchmark                      Mode  Cnt      Score      Error  Units
[info] JMHSample.measureForMeth      thrpt   20  10260.024 ± 1172.508  ops/s
[info] JMHSample.measureMapMeth      thrpt   20  10383.953 ±  843.856  ops/s
[success] Total time: 567 s
```

JMHのスループットで比較したところ、`for`式と`map`関数ではほとんど差がない状態が若干`map`が良いという結果でした。
しかし、Errorの範囲によってScoreの結果も変わるのでこの程度の差であれば、ほとんど性能は変わらなそうです。

繰り返し構文が一段の場合でも、コードで`for`式が多い場合は`for`式で書いた方が読みやすいと思いますし、どちらが良いか一概には言えないなと思いました。