ゼロから始めるScala (制御構文編)

# Scala をはじめよう 第2回目

* [前回記事 - ゼロから始めるScala (環境構築 mac)](https://qiita.com/daichildren98/items/ea0b5eef5ffd3e37fdd3)

今回はScalaの制御構文をまとめてみたい。

## 実行環境 (review)

前回構築した`scala`or`sbt`を用いる

### REPL起動

```
# --- scala ---
$ scala

Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 11.0.7).
Type in expressions for evaluation. Or try :help.
scala>

# --- sbt ---
$ sbt console
...
[info] Starting scala interpreter...
Welcome to Scala 2.12.10 (OpenJDK 64-Bit Server VM, Java 11.0.7).
Type in expressions for evaluation. Or try :help.

scala>
```


### ソースファイルを作成して実行

拡張子は`.scala`

```source.scala
object Main {
  def main(args: Array[String]): Unit = {
	println("scala program")

	// 以下に処理を記述していく
  }
}
```

* コンパイルして実行

```
$ scalac source.scala
$ scala Main
```

* コンパイルしないで実行

```
$ scala source.scala
```



### sbt でコンパイル, 実行

何かフォルダを作っておくと良いと思います。
(名前はmyfolderとしましたが、なんでも良いです、対応する部分は読み替えてください)

```
$ mkdir myfolder
$ cd myfolder
```

myfolder下にソースファイルを書いて置きます。

```source.scala
object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, Scala World!")
    
    // 以下に、処理を記載していく
  }
}
```

myfolder下に`sbt`の設定ファイルを置きます

```build.sbt
scalaVersion := "2.12.10"

scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlint")
```

```
# フォルダたちの配置はこんな感じ
./myfolder (current working directory)
├── source.scala
└──build.sbt
```


`sbt`を起動

```
[info] ...
sbt:myfolder>
```

`run`コマンドで実行

```
sbt:myfolder> run
[info] Compiling 1 Scala source to ...
[info] Running source
Hello, Scala World!
[success] Total time: 1 s, completed 2015/02/09 15:44:44`
```

`run`コマンドでは、mainメソッドを持つオブジェクトを探して実行してくれるらしい。

**--- review ここまで ---**

## 制御構文

### 「構文」、「式」、「文」について

* 「構文」: 予約語などを用いて、文法に従い、書いていったもの。
* 「式」: 評価(が成功)すると値になるもの。
* 「文」: 評価しても値にならないもの。

#### 「構文の例」
* classやobjectの定義

```source.scala
class ClassName {
    ...
}


object ObjectName {
  def main(args: Array[String]): Unit = {
    ...
  }
}
```

* 制御「構文」 (後述 - 本題)


#### 「式の例」

```scala
// 計算や値
scala> 1 + 2
val res0: Int = 3  // <= 値になっている

scala> "Hello"
val res1: String = Hello  // <= 値になっている
```

#### 「文の例」

```scala
// 変数/定数の定義
var x: Int = 100
val a: double = 2.0
```

### 複数の式のまとまり

Scalaで、複数の「式」を`{ }`でくくると、「くくったもの全体」を「大きな1つの式」と見なす。

並べられた複数の式は、

* 改行か、セミコロン `;` で区切られ
* 上(前)から順に評価される。
* また、「最後の式を評価した値」が「全体の大きな1つの式」の返り値

となる。

例:

```scala
scala> { println("hoge"); println("piyo"); 3 + 5; }
hoge
piyo
val res0: Int = 8  // <= 最後の式を評価した値が返り値になる。
```

### 本題

#### if式

```scala
if (条件 /* なるべく肯定系で書く */ ) {
  // 条件に合った時の処理
} else {
  // 条件に合わなかった時の処理
}

```

* if文と同じく「条件に合致するかしないか」で分岐を行う。
* 分岐後の処理の部分には、その場合に落ちた時に評価される"式"を記述する。
* `else`の条件は省略可能
    * 省略した場合は `Unit型の ()` が返って来る。
    * Unit型とは、いわゆる`Void型`に対応する型。雑にいうと「空っぽ」ってこと(だと思っている)。

```scala
var age: Int = 17

if (age < 18) {  // 18歳未満は未成年。
  "未成年です。"
} else {         // 18歳以上ならば成人済み
  "成人です。"
}

// ちなみに、評価すると、「res1: String = "未成年です。"」が返って来る。

// 余談
//  あれ?結局成人って18歳になったんですよね? 20歳でしたっけ?
```

**(大事) Scala では、「制御構文」は全て「式」
つまり、必ずなんらかの値を返す。**

#### while式

```scala
while (条件) {
  // 条件が真のあいだ、繰り返す処理
  //  無限ループを生まないように注意...(自戒)
}
```

* 条件が真のあいだ(while)、処理を繰り返す(do)構文
* while式も「式」なので、値を返すが、返すべき値がないので、`Unit型の ()`が返って来る。

```scala
var day: Int = 0
// day: Int = 0

while(day <= 7) {  // 一週間経過するまで、日にちをインクリメント
  println(day + " 日経ちました")
  day = day + 1
}

// 出力
// 0 日経ちました
// 1 日経ちました
// 2 日経ちました
// 3 日経ちました
// 4 日経ちました
// 5 日経ちました
// 6 日経ちました
// 7 日経ちました
```

* dowhile という構文もある

```scala
// 0 から 9 まで数え上げる
var i: Int = 0

do {
  println("i=" + i)
  i = i + 1
} while(i<10)

// 出力
// i=0
// i=1
// i=2
// i=3
// i=4
// i=5
// i=6
// i=7
// i=8
// i=9
```

<font color='gray'>dowhileは、コードを読む時に目線が下から上に動かさねばならず、不自然なため、使うことが推奨されないという説もある(あった気がする)。</font>

#### return式

* 処理の途中で抜け出して、値を返すのに使われる構文
* Scalaでは、式は必ず返り値を持つので、他の多くのプログラミング言語と違い、returnは必ず必要という訳ではないのだが、上手に使うと読みやすいコードを作ることができる(こともある)。

<!-- TODO: 例文(code)をつける -->

#### for式

```scala
for (繰り返し変数 <- 繰り返し範囲) {
  // 繰り返し範囲にある間、繰り返される処理
}
```

* 「for文」の拡張とも言える構文
* 多重ループも1文でかける。
* ループの条件指定が2通りある(`to` と `until`)

```scala
// to:
//  「x <- a to b」 で「a以上b以下」の範囲でループする。
for(x <- 1 to 5){
  println("x = " + x)
}

// (出力)
// x = 1
// x = 2
// x = 3
// x = 4
// x = 5
```

```scala
// until:
//  「y <- a to b」 で「a以上b未満 (b-1以下)」の範囲でループする。
for(y <- 1 until 5){
  println("y = " + y)
}

// (出力)
// y = 1
// y = 2
// y = 3
// y = 4
```

* 二重ループ

```scala
// x: 1 ~ 5 まで & y: 1 ~ 4 まで総当たりの順列
for(x <- 1 to 5; y <- 1 until 5){
  println("x = " + x + ", y = " + y)
}

/* 出力 ここから
x = 1, y = 1
x = 1, y = 2
x = 1, y = 3
x = 1, y = 4
x = 2, y = 1
x = 2, y = 2
x = 2, y = 3
x = 2, y = 4
x = 3, y = 1
x = 3, y = 2
x = 3, y = 3
x = 3, y = 4
x = 4, y = 1
x = 4, y = 2
x = 4, y = 3
x = 4, y = 4
x = 5, y = 1
x = 5, y = 2
x = 5, y = 3
x = 5, y = 4
出力 ここまで */
```

* ループ内に条件指定することもできる。

```scala
// x: 1 ~ 5 まで & y: 1 ~ 4 まで総当たりの順列で、
//  x と y が同じ値を持つ、ならば、println()する
for(x <- 1 to 5; y <- 1 until 5; if x == y){
  println("x = " + x + ", y = " + y)
}

x = 1, y = 1
x = 2, y = 2
x = 3, y = 3
x = 4, y = 4

/* 出力
x = 1, y = 1
x = 2, y = 2
x = 3, y = 3
x = 4, y = 4
確かに指定した条件の通りになっている */
```

### FizzBuzz

ここまでの制御構文の知識があれば[FizzBuzz](https://ja.wikipedia.org/wiki/Fizz_Buzz)が書けそうだったので、書いてみました。

```Main.scala
object Main {
  def main(args: Array[String]): Unit = {

    for (i <- 1 to 100) {
      if (i%15 == 0) {
        println(i + ": FizzBuzz")
      } else if (i%3 == 0) {
        println(i + ": Fizz")
      } else if (i%5 == 0) {
        println(i + ": Buzz")
      } else {
        println(i)
      }

    }
  }
}
```

```実行
scala Main.scala
```

もしくは

```実行2
$ scalac Main.scala
$ scala Main
```

とりあえず、ここまで。
奥が深そうなので、追加で勉強したことがあったら順次記載していきたいと思います。


## 参考

* [ドワンゴ 新卒エンジニア向けの研修資料](https://scala-text.github.io/scala_text/sbt-compile-execute.html)
* [Scalaプログラムの基本（まとめ）](http://www2.nagano.ac.jp/hiraoka/BP/14_4.html)
* [第一章：猿でも分かるScala!](https://qiita.com/natsuisexy/items/59c2190561e08b85d1de)  
<font color='gray'>どうして猿を引き合いに出したがるのか...<font>