Scalaに入門してみる (随時更新予定)

# 概要

Scalaに入門してみたので、その軌跡を記録したいと思います。
(途中でやめてしまうかもしれないですが...)

# はじめに

### 環境設定

MacでのScalaの環境設定は、良い記事がたくさんあるので「Mac Scala 環境構築」などで調べてみてください。
Scalaは `2.12.8` を利用しています (2018/12/29時点)

### 参考にしたサイト

- [Scala研修テキスト - dwango on GitHub](https://dwango.github.io/scala_text/)
    - 初心者向け(?)のテキストということもあり、よくまとまっていて非常に参考になりました。
- [Scala Standard Library](https://www.scala-lang.org/api/current/index.html) 
    - 標準ライブラリのドキュメントっぽいです。

### S-99をやってみる

[S-99](http://aperiodic.net/phil/scala/s-99/)というScalaの練習問題を解きながら、Scalaや関数型プログラミング言語への理解を深めていこうと思い、少しずつですが取り組んでみようと思います。

### この記事の見方

やった問題は [Github](https://github.com/RINDO/scala_ex/tree/master/src/main/scala)に載せています。
ここでは、問題を解いた時点で発見のあったところを中心に記事にしたいと思います。

※ P28まで回答済 (2019/1/4)

# S-99

## P07

```scala 
// P07 (**) Flatten a nested list structure.
// Example:
scala> flatten(List(List(1, 1), 2, List(3, List(5, 8))))
res0: List[Any] = List(1, 1, 2, 3, 5, 8)
```

複数階層になっているリストが混じっているリストをフラットにする、という問題です。

### 回答

Rubyであれば `flatten` で以下のように書けるので、同じ気持ちでいたのですが...
Scalaではエラーが出てしまいました🤔

```ruby
irb(main):001:0> [[1, 1], 2, [3, [5, 8]]].flatten
=> [1, 1, 2, 3, 5, 8]
```

Scalaの[flattenの定義](https://www.scala-lang.org/api/current/scala/collection/immutable/List.html#flatten[B]:List[B])を確認してみます。

```scala 
def flatten[B]: List[B]
// [use case]
// Converts this list of traversable collections into a list formed by the elements 
// of these traversable collections.
// 
// The resulting collection's type will be guided by the static type of list. For example:
```

んー、まぁわからないんですけどねw 「Converts this list of traversable collections」と書いてあります。 traversableとはなんでしょうか..
[Scalaの日本語ドキュメント](https://docs.scala-lang.org/ja/overviews/collections/trait-traversable.html)によると、 `走査可能 (Traversable)トレイトはコレクション階層の最上位に位置する。` と書いてありました。つまり、ArrayとかListとかmapやflatMapを使えるようなもののリストでないとダメなのでしょうか？
だとすると、1のような整数はNGということになりそうですよね。

```scala 
scala> List(List(1,2,3), List(1,2,3)).flatten
res1: List[Int] = List(1, 2, 3, 1, 2, 3)

scala> List(List(1,2,3), Set(1,2)).flatten
res2: List[Int] = List(1, 2, 3, 1, 2)

scala> List(List(1,2,3), 1, 2).flatten
<console>:12: error: No implicit view available from Any => scala.collection.GenTraversableOnce[B].
       List(List(1,2,3), 1, 2).flatten
                               ^
```

(もう答え見てしまっていますが...w)
では、配列を生成してからflattenすればいいのでしょうか？ (= flat + map = flatMap)

```scala
final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]
// [use case]
// Builds a new collection by applying a function to all elements 
// of this list and using the elements of the resulting collections.
```

リストの各要素に対して、 引数f(A型の変数を引数にとり、 B型のGenTraversableOnce型を返す)をとり、B型のリストを生成する関数と見えます。
GenTraversableOnceとは、先ほどのTraversableの親戚のようなもの(多分)で、リストを生成すればよいみたいです。

ここで、引数として与えられているのは `List(List(1, 1), 2, List(3, List(5, 8)))` なので、以下のパターンがありそうです。

1. Int型の配列
2. Int型

なので、flatMapの内容を以下のようにしてあげたらよさそうでした。

```scala
def flatten(list: List[Any]): List[Any] = list flatMap {
  case head:List[_] => flatten(head)
  case e            => List(e)
}
```

ここで登場している `head:List[_]` というのは、 1行目で `list flatMat { ...`  となっているので、listの中の1要素に対してのパターンマッチであり、 `head:List[_]` というのは「変数名: head」で、headの型が「List[_]」という意味です。つまり、リストが来たら再度自身を呼び出して次のパターンにマッチさせています。
最終的に `List(List(1, 1), 2, List(3, List(5, 8)))` は `List(List(1), List(1), List(2), List(3), List(5), List(8))` ということになり、それに対してflattenが呼び出されており (多分)、 `List(1, 1, 2, 3, 5, 8)` となります。

## P22

```scala 
// P22 (*) Create a list containing all integers within a given range.
// Example:
scala> range(4, 9)
res0: List[Int] = List(4, 5, 6, 7, 8, 9)
```

回答

```scala
object P22 {
  def range(from: Int, to: Int): List[Int] = {
    def rangeR(end: Int, ls: List[Int]): List[Int] = {
      if (from > end) ls
      else rangeR(end-1, end :: ls)
    }
    rangeR(to, Nil)
  }

  def main(args: Array[String]): Unit = {
    println(range(4, 9))
    println(range2(4, 9))
  }
}
```

scalaでは、関数内に関数を定義することができるようです。
[Nested Methods](https://docs.scala-lang.org/tour/nested-functions.html)

## P28:a

```scala
// P28 (**) Sorting a list of lists according to length of sublists.
// a) We suppose that a list contains elements that are lists themselves. The objective is to sort the elements of the list according to their length. E.g. short lists first, longer lists later, or vice versa.
// Example:

scala> lsort(List(List('a, 'b, 'c), List('d, 'e), List('f, 'g, 'h), List('d, 'e), List('i, 'j, 'k, 'l), List('m, 'n), List('o)))
res0: List[List[Symbol]] = List(List('o), List('d, 'e), List('d, 'e), List('m, 'n), List('a, 'b, 'c), List('f, 'g, 'h), List('i, 'j, 'k, 'l))
```

回答

```scala
import P26.combinations

object P27 {
  def group3[A](list: List[A]): List[List[List[A]]] = {
    for {
      a <- combinations(2, list)
      b <- combinations(3, list diff a)
      c <- combinations(4, list diff b)
    } yield List(a, b, c)
  }.toList

  def main(args: Array[String]): Unit = {
    val list = List("Aldo", "Beat", "Carla", "David", "Evi", "Flip", "Gary", "Hugo", "Ida")
    group3(list).foreach(println)
  }
}
```

scalaのfor文も非常に強力だなと思いました（多分10%も理解できてない)
[こちらの記事](https://qiita.com/harry0000/items/e37ca3bfb68df839bf55)が非常に参考になりました。ありがとうございます。