Scala入門 基礎編「Scalaの書き方を理解しよう」

## 前提

Scalaの勉強で覚えたことを備忘録的にズラズラ書いていきます。
細かい説明は省略します。

## Scala入門

### プロジェクト作成

1. ディレクトリを作成
2. Scalaのバージョン情報を書き込んだ設定ファイルを用意
3. sbt起動

```bash
$ mkdir scala_project
$ cd scala_project
$ echo 'scalaVersion := "2.12.4"' > build.sbt
$ sbt
...
sbt:scala_project>
```

### 変数 - val と var の使い分け

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // 変数
    // - val: 値の再代入ができない immutable
    // - var: 値の再代入ができる   mutable

    // val msg: String = "Hello world"
    // val msg = "Hello world" // 型推論（データ型が自明の場合）
    var msg = "Hello world"
    msg = "Hello world again?"
    println(msg)
  }
}
```

### データ型

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // 整数 Byte Short Int Long
    val i = 5 // この場合は型推論で Int となる
    val l = 5555555555L
    // 浮動小数点数 Float Double
    val d = 32535.5
    val f = 234.34F
    // 文字 char 
    val c = 'a'
    // 文字列 String
    val s = "Hello"
    // 論理値 Boolean
    val flag = true // or false
    // 特殊文字 \n: 改行、 \t: タブ
    val msg = "Hello w\norl\td"
    println(msg)
  }
}
```

### 演算

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // 数値
    // + - * / %
    val x = 10
    println(x / 3) // 3
    println(x / 3.0) // 3.3333...
    println(x % 3) // 1

    var y = 5
    // y = y + 5
    y += 5 // 10

    // 文字列
    val s = "Hello "
    println(s + "world")

    // 論理値
    // and or not
    // && || i
    val flag = true
    println(!flag) // false
  }
}
```

### 文字列

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    val name = "taguchi"
    val score = 80
    val height = 158.3

    println(s"name: $name, score: $score, height: $height")
    println(s"name: $name, score: ${score + 10}, height: $height")

    println(f"name: $name%10s, score: $score%10d, height: $height%10f")
    println(f"name: $name%10s, score: $score%-10d, height: $height%10f")
    println(f"name: $name%10s, score: $score%-10d, height: $height%10.2f")
  }
}
```

### if

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    val score = 70

    // > >= < <= == != && || !
    // if (score > 80) println("Great!")
    // else if (score > 60) println("Good!")
    // else println("so so ...")

    // Scalaではif文は値を返す
    val result = 
      if (score > 80) "Great!"
      else if (score > 60) "Good!"
      else "so so ..."
    println(result)
  }
}
```

### match

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // match
    val signal = "pink"
    val result = signal match {
      case "red" => "stop"
      case "blue" | "green" => "go"
      case "yellow" => "caution"
      // case _ => "wrong signal" // 他の言語で言うdefault
      case other => s"wrong signal: ${other}" // 検索対象の値をそのまま変数で受け取る事もできる
    }
    println(result)
  }
}
```

### while

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // while
    // 0 - 9
    var i = 100

    // while (i < 10) { // 最初に判定を行う
    //   println(i)
    //   i += 1 // Scalaでは++の記法はないので注意
    // }

    do {
      println(i)
      i += 1
    } while (i < 10) // 後判定
  }
}
```

### for

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // for (ジェネレータ) 処理
    // for (変数 <- データの集合) 処理
    // 0 to 3, 0 until 4
    // for (i <- 0 to 3) println(i)
    // for (i <- 0 to 3; j <- 0 to 3) println(s"$i, $j")
    // for (i <- 0 to 3; j <- 0 to 3 if i != j) println(s"$i, $j")

    val result = for (i <- 0 to 3; j <- 0 to 3 if i != j) yield s"$i, $j" // yield = イールド
    for (element <- result) println(element)
  }
}
```

### メソッド

```scala
object MyApp {
  // method
  // def sayHi: Unit = { // 意味のある値を返さない場合の返り値の型
  def sayHi: String = {
    // println("hi!")
    // return "hi!"
    "hi!" // Scalaでは最後に評価された値が単に返されるので、returnを省略することもできる
  }

  def main(args: Array[String]): Unit = {
    // sayHi
    println(sayHi)
  }
}
```

### 引数の使い方

```scala
object MyApp {
  // method
  def sayHi(name: String = "taguchi", age: Int = 23): Unit = { 
    println(s"hi! $name ($age)")
  }

  def main(args: Array[String]): Unit = {
    sayHi("bob", 35)
    sayHi("tom", 55)
    sayHi()
    sayHi(age = 18, name = "steve")
  }
}
```

### クラス

```scala
// Class
//   user: User
//   i: Int
// - 変数 / フィールド
// - メソッド

class User {
  val name = "my name"
  def sayHi() = println("hi!") // メソッドの返り値の型は省略（型推論）、かつメソッドの中身も一行なので {} 省略
}

object MyApp {
  def main(args: Array[String]): Unit = {
    // val user: User = new User
    val user = new User // 型推論がきくので型宣言を省略しても良い
    println(user.name)
    user.sayHi()
  }
}
```

### コンストラクタ引数を使う

```scala
// class User(_name: String) {
class User(val name: String) { // コンストラクタ引数をそのままフィールドにする場合にvalやvarを付けてあげる
  // val name = _name
  // def sayHi() = println("hi" + this.name)
  def sayHi() = println("hi! " + name) // どこのnameか自明の場合はthis省略可
}

object MyApp {
  def main(args: Array[String]): Unit = {
    // val tom: User = new User("tom")
    val tom = new User("tom") // インスタンス
    println(tom.name)
    tom.sayHi()
  }
}
```

### 継承

```scala
// User         -> AdminUser へ継承
// 親クラス      -> 子クラス という
// スーパークラス -> サブクラス ともいう

class User(val name: String) {
  def sayHi() = println("hi! " + name)
}

class AdminUser(name: String, val age: Int) extends User(name) {
  def sayHello() = println("hello! " + name + "(" + age + ")")
  override def sayHi() = println("[admin] hi! " + name) // 親メソッドの上書き
}

object MyApp {
  def main(args: Array[String]): Unit = {
    val bob = new AdminUser("bob", 23)
    println(bob.name)
    println(bob.age)
    bob.sayHi()
    bob.sayHello()
  }
}
```

### パッケージでクラスを管理

```scala:パッケージファイル
package com.scala_lessons.model

class User(val name: String) {
  def sayHi() = println("hi! " + name)
}

class AdminUser(name: String, val age: Int) extends User(name) {
  def sayHello() = println("hello! " + name + "(" + age + ")")
  override def sayHi() = println("[admin] hi! " + name) // 親メソッドの上書き
}
```

```scala:パッケージを利用するファイル
import com.scala_lessons.model.User
import com.scala_lessons.model.AdminUser
// import com.lesson17.model.{User, AdminUser} // 複数呼び出す場合
// import com.lesson17.model._                 // Package内のすべてのクラスを呼び出す場合

object MyApp {
  def main(args: Array[String]): Unit = {
    // val tom = new com.scala_lessons.model.User("tom")
    val tom = new User("tom")
    val bob = new AdminUser("bob", 23)
  }
}
```

### アクセス修飾子

```scala
// アクセス修飾子
// (public)  <- 何もつけないとデフォルトで公開となる
// private   <- そのクラスからのみアクセスできる
// protected <- そのクラスのサブクラスからのみアクセスできる

class User {
  // val name = "user"
  // protected val name = "user"
  private val name = "user"
  def sayHi() = println("hi! " + name)
}

class AdminUser extends User {
  override def sayHi() = println("[admin] hi! " + name) // nameがprivateなのでエラーになる
}

object MyApp {
  def main(args: Array[String]): Unit = {
    val user = new User
    val adminUser = new AdminUser
    // println(user.name)
    user.sayHi()
    adminUser.sayHi()
  }
}
```

### final修飾子

```scala
// final
// - class: 継承できない
// - members: overrideできない

class User {
// final class User {
  // final val name = "user"
  val name = "user"
  def sayHi() = println("hi! " + name)
}

class AdminUser extends User {
  override val name = "user"
  override def sayHi() = println("[admin] hi! " + name)
}

object MyApp {
  def main(args: Array[String]): Unit = {
    val user = new User
    val adminUser = new AdminUser
    println(adminUser.name)
  }
}
```

### オブジェクト

```scala
// object

object User { // 同名の場合、コンパニオンオブジェクト
  def getInfo() = println("User Object")
  def apply(name: String) = new User(name) // ファクトリーメソッド
}

class User (val name: String) { // 同名の場合、コンパニオンクラス
  def sayHi() = println("hi! " + name)
}

object MyApp {
  def main(args: Array[String]): Unit = {
    User.getInfo()
    // val tom = new User("tom")
    // val tom = User.apply("tom") 
    val tom = User("tom") // 省略して書ける
    Predef.println(tom.name) // printlnはPredefオブジェクトのメンバー
  }
}
```

### 抽象クラスを作ってみる

```scala
// 抽象クラス
// User: 抽象クラス
// - Japanese: 具象クラス
// - American: 具象クラス

abstract class User {
  def sayHi()
}

class Japanese extends User {
  def sayHi() = println("こんにちは！")
}

class American extends User {
  def sayHi() = println("hi!")
}

object MyApp {
  def main(args: Array[String]): Unit = {
    val aki = new Japanese
    val tom = new American
    aki.sayHi()
    tom.sayHi()
  }
}
```

### トレイトで機能を合成する

```scala
// trait

trait Printable {
  def print() = println("now printing ...")
}

trait Sharable {
  def share() = println("now sharing ...")
}

class User extends Printable with Sharable

object MyApp {
  def main(args: Array[String]): Unit = {
    val user = new User
    user.print()
    user.share()
  }
}
```

### トレイト多重継承時のメンバー衝突を解決する

```scala
// trait
// 多重継承時の同名メンバー衝突回避策

// - その1：継承するクラス側でオーバーライド（してどちらのメンバーを使用するか選択）する
// trait Printable {
//   def print() = println("now printing ...")
//   def getInfo() = println("print!")
// }

// trait Sharable {
//   def share() = println("now sharing ...")
//   def getInfo() = println("share!")
// }

// class User extends Printable with Sharable {
//   override def getInfo() = super[Printable].getInfo()
// }

// - その2：共通トレイトを作成しメンバーに優先順位をつける（後から合成したトレイトのメンバーにてオーバーライドされる）
trait common {
  def getInfo()
}

trait Printable extends common {
  def print() = println("now printing ...")
  override def getInfo() = println("print!")
}

trait Sharable extends common {
  def share() = println("now sharing ...")
  override def getInfo() = println("share!")
}

class User extends Printable with Sharable

object MyApp {
  def main(args: Array[String]): Unit = {
    val user = new User
    user.getInfo() // Sharableの方が後からwithしているので「share!」と表示される
  }
}
```

### 型をパラメータ化する

```scala
// 型パラメータ

// class MyInt {
//   def getThree(i: Int): Unit = println(s"$i $i $i")
// }
class MyData[T] {
  def getThree(i: T): Unit = println(s"$i $i $i")
}

object MyApp {
  def main(args: Array[String]): Unit = {
    // val mi = new MyInt
    // mi.getThree(3)
    val i = new MyData[Int]
    i.getThree(5)
    val s = new MyData[String]
    s.getThree("hello")
  }
}
```

### 関数オブジェクト

```scala
// 関数　Input -> Output

object MyApp {
  // method
  def multi(a: Int, b: Int) = a * b

  // 関数オブジェクト
  // - 引数 => 処理
  // val multiFunc: (Int, Int) => Int = (a: Int, b: Int) => a * b
  // val multiFunc = (a: Int, b: Int) => a * b
  val multiFunc = (_: Int) * (_: Int) // 処理の中で引数を一度しか使わない場合はプレースホルダーが使える

  def main(args: Array[String]): Unit = {
    // println(multiFunc(3, 5)) // 15
    println(multiFunc(2, 4)) // 8
  }
}
```

### 関数のカリー化

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // 関数のカリー化
    val multiFunc        = (a: Int, b: Int) => a * b
    val multiFuncCurried = (a: Int) => ((b: Int) => a * b)

    // println(multiFunc(3, 5))
    // println(multiFuncCurried(3)(5))

    val double  = multiFuncCurried(2) // double  = (b: Int) => 2 * b
    val tripple = multiFuncCurried(3) // tripple = (b: Int) => 3 * b

    println(double(5))  // 2 * 5 = 10
    println(tripple(5)) // 3 * 5 = 15
  }
}
```

### 部分適用

```scala
object MyApp {
  // 部分適用
  def msg (from: String, to: String, text: String) = s"($from -> $to): $text"

  def main(args: Array[String]): Unit = {
    // println(msg("taguchi", "fkoji", "OK!"))
    // println(msg("sugita", "fkoji", "Great!"))
    // println(msg("tanaka", "fkoji", "Good!"))

    val msgToFkoji = msg(_: String, "fkoji", _: String) // 部分的に引数をプレースホルダーにする
    val msgFunc    = msg _                              // すべての引数をプレースホルダーにする

    println(msgToFkoji("taguchi", "OK!"))
    println(msgToFkoji("sugita", "Great!"))
    println(msgToFkoji("tanaka", "Good!"))
  }
}
```

### Tuble：タプル

```scala
object MyApp {

  def swap(a: Int, b: Int) = (b, a)

  def main(args: Array[String]): Unit = {
    // tuple
    // タプルはなんでも入れられるが、23個以上は入れられない制限あり
    
    // val data = (12, "taguchi", 52.4) // tupleは括弧で囲う
    // println(data._1)
    // println(data._2)
    // println(data._3)

    val(x, y) = swap(30, 20) // 返り値がタプル（複数の値）の場合は、それを受ける変数もタプル型にする
    println(x) // 20
    println(y) // 30
  }
}
```

### List：リスト

```scala
object MyApp {

  def main(args: Array[String]): Unit = {
    // Collection（データの集合）
    // - Seq: List （シークエンス： 順番を持つデータ集合）
    // - Set       （セット：順番を持たない＆重複しないデータ集合）
    // - Map       （マップ：キーと値でデータを管理していく集合）

    // val scores = List(200, 300, 400)
    // val scores = List[Int](200, 300, 400) // 型は省略可

    // val scores = Nil // 空のリストを作成
    // val scores = 200 :: Nil // からのリストの先頭に値を追加
    val scores = 200 :: 300 :: 400 :: Nil
    println(scores.length)  // 3
    println(scores.isEmpty) // false
    println(scores.head)    // 200
    println(scores.tail)    // List(300, 400)
    println(scores(1)) 　　　// 300
  }
}
```

### Set：セット

```scala
object MyApp {

  def main(args: Array[String]): Unit = {
    // Collection（データの集合）
    // - Seq: List （シークエンス： 順番を持つデータ集合）
    // - Set       （セット：順番を持たない＆重複しないデータ集合）
    // - Map       （マップ：キーと値でデータを管理していく集合）

    val answers = Set(5, 3, 8, 5)
    println(answers)             // 5, 3, 8
    println(answers.contains(3)) // true
    println(answers(3))          // true .contains()の省略形

    val set1 = Set(1, 3, 5, 8)
    val set2 = Set(3, 5, 8, 9)

    println(set1 & set2)  // 積集合：& or intersect ※両方に入っている要素を集めたもの
    println(set1 | set2)  // 和集合：| or union     ※すべての要素が最低限一つ含まれていること
    println(set1 &~ set2) // 差集合：$~ or diff     ※ある集合の中から別の集合に属する要素を取り去って得られる集合のこと
  }
}
```

### Map：マップ

```scala
object MyApp {

  def main(args: Array[String]): Unit = {
    // Collection（データの集合）
    // - Seq: List （シークエンス： 順番を持つデータ集合）
    // - Set       （セット：順番を持たない＆重複しないデータ集合）
    // - Map       （マップ：キーと値でデータを管理していく集合）
    //   - Key -> value

    val sales = Map("taguchi" -> 200, "fkoji" -> 400)
    println(sales("taguchi"))                  // 200
    // println(sales("not match"))            // error
    println(sales.contains("not match"))      // false
    println(sales.getOrElse("not match", -1)) // -1
  }
}
```

### Mutableなコレクションを使う

```scala
import scala.collection.mutable.{ Map => MutableMap }

object MyApp {

  def main(args: Array[String]): Unit = {
    // Collection（データの集合）
    // - Seq: List （シークエンス： 順番を持つデータ集合）
    // - Set       （セット：順番を持たない＆重複しないデータ集合）
    // - Map       （マップ：キーと値でデータを管理していく集合）
    
    // Immutable 変更不可（関数型プログラミングで推奨されている）
    val scores  = Map("taguchi" -> 40, "fkoji" -> 80)
    val scores2 = scores.updated("taguchi", 60) // 値を変更した別のMapを返してくれる

    // Mutable 変更可
    val scores3 = MutableMap("taguchi" -> 40, "fkoji" -> 80)
  }
}
```

### map, filter, foreachを使う

```scala
object MyApp {

  def main(args: Array[String]): Unit = {
    val prices = List(53.2, 48.2, 32.8)

    // prices.map((n: Double) => n * 1.08)
    // prices.map(n => n * 1.08)
    prices.map(_ * 1.08).filter(_ > 50).foreach(println)
  }
}
```

### case classを使ったパターンマッチング

```scala
// case class: Immutable
// -> クラスの構造やフィールドの内容でパターンマッチさせることができる

case class Point(x: Int, y: Int) // コンストラクタ引数にはval, varは不要

object MyApp {

  def main(args: Array[String]): Unit = {
    val points = List(
      Point(5, 3),
      Point(0, 0),
      Point(3, 4)
    )

    points.foreach(_ match {
      case Point(0, 0) => println("origin!")
      case Point(5, _) => println("right edgh!") // yは使わないためプレースホルダーを付けておく
      case Point(x, y) => println(s"$x:$y")
    })
  }
}
```

### option型で値を返す

```scala
object MyApp {
  def main(args: Array[String]): Unit = {
    // Error
    // Option 何らかのエラーが起きた際にnullや例外を投げる代わりに値を返す
    // - some 値が存在した時
    // - None 値が存在しない時

    val scores = Map("taguchi" -> 50, "fkoji" -> 80)
    // println(scores("not match")) // getOrElse, contains

    scores.get("not match") match { // Mapのgetメソッドの返り値がOption型になっている
      case Some(v) => println(v)
      case None    => println("key not found")
    }
  }
}
```

### Either型でエラー処理

```scala
object MyApp {

  // Error
  // Either エラーの内容を返す
  // - Right 
  // - Left

  def div(a: Int, b: Int): Either[String, Int] = {
    if (b == 0) Left("Zero div error!")
    else Right(a / b)
  }

  def main(args: Array[String]): Unit = {
    // div(10, 0) match {
    div(10, 3) match {
      case Right(n) => println(n)
      case Left(s)  => println(s)
    }
  }
}
```