02.Scala で階乗の計算　Scala基礎コース

# 目次

* 【実習】ファイルから Scala を実行する
* 【講義】Scala で作成するアプリケーション
* 【実習】sbt を立ち上げたままでの実行
* 【実習】距離を計算するプログラムの実装
* 【講義】距離を計算するプログラムの解説
* 【実習】距離を計算するプログラムの実行
* 【講義】Scala における数値の表現と計算
* 【実習】階乗を計算するプログラムの実装
* 【講義】階乗を計算するプログラムの解説

# 【実習】ファイルから Scala を実行する

今回は Scala に簡単な数学の問題を解かせることができるようにする。
まずは、1からその数値までを掛け合わせる階乗の計算ができるようにする。

今回はファイルに記述した Scala のプログラムを実行できるようにする。

`scala-study` フォルダ内に、`Main.scala` というファイルを作成しよう。

ターミナを起動して、

```:Terminal
$ cd ~/workspace/scala-study
$ echo 'object Main extends App { println("Hello Scala") }' > Main.scala
```

では、VS Code エディタでファイルを開き、中身を確認しよう。
`Main.scala` の内容が、

```scala:Main.scala
object Main extends App { println("Hello Scala") }
```

以上のようになっていれば問題ない。

なお、VS Code ではメニューの「表示」の「コマンドパレット」を選択し、最初に表示される `>` をバックスペースで消した後

```
ext install scala
```

以上を入力して、Enter キーを押すと、左側に拡張機能の検索結果が表示される。
そこで表示されている `Scala Syntax (official)` をインストールする。
インストール後に再読み込みボタンをクリックすることで、Scala のシンタックスハイライトを利用できるようになる。

VS Code の Scala の拡張のインストール後、`Main.scala` を開き直すと、部分毎に色分けされているだろう。

エディタの準備ができたところで `scala-study` フォルダにてこのプログラムを実行する。

PowerShell またはTerminalで、`scala-study` フォルダに移動し、

```:Terminal
$ sbt run
```

以上のコマンドを実行する。
これがプログラムをビルドして実行させるコマンド。

```:Terminal
[info] running Main 
Hello Scala
[success] Total time: 5 s, completed 2021/07/25 13:51:56
```

最終的に以上のように表示されれば成功。

なお、sbt を終了する際には、

```
exit
```

と入力する。
`exit` を入力して sbt を終了させよう。

# 【講義】Scala で作成するアプリケーション

ソースコードを解説する。

```scala:Main.scala
object Main extends App { println("Hello Scala") }
```

これは Scala で記述されたアプリケーションのコード。

まずファイル名の `Main.scala` について説明する。
Scala のプログラムを書くファイルは、基本的に大文字始まりで単語の区切りを大文字にするアッパーキャメルケースを利用する。
これは、Java や JavaScript でもよく利用されているファイル名の命名規則。

また、`Main.scala` の Main という命名は、プログラムの主となる処理のことをメイン処理というため、その英語である Main を利用している。

なお、Scala も含めプログラミング言語の C や Java においても `main` という名前の付けられた関数は特別な意味を持ち、ファイルが実行された際一番最初に呼ばれる関数となる。
ただし、Scala では main 関数を直接記述するのではなく App トレイトを利用した方が簡潔にかけるため、こちらを利用することが多い。

`.scala` はこのファイルがプログラミング言語の Scala で記述されていることを示すための拡張子。

次にファイルの中身について見ていこう。

```scala
object Main
```

まず以上の記述では、Main という名前の**オブジェクト**を宣言している。

Scala のオブジェクトとは何なのかについては、また別の回で詳しく説明する。
ここでは、Main という「何かしらの状態や処理を保持できる構造」が作られると理解しておこう。

次に、

```scala
object Main extends App { }
```

以上の `extends App` の部分。

この記述で、Main という名前のアプリケーションを作ることができる。

この記述の意味は、Main というオブジェクトが、App という **トレイト** を **ミックスイン** していることを宣言している。

「トレイト」とは振る舞いを定義するもので、「ミックスイン」とはその振る舞いを「オブジェクト」に対して追加すること。
[App](https://www.scala-lang.org/files/archive/api/2.12.6/scala/App.html)は、その後に宣言された処理をアプリケーションとして実行するための振る舞いを提供してくれる「トレイト」。
そのため、この記述では App の振る舞いを含んだ Main という「オブジェクト」ができることになる。

これらの「オブジェクト」「トレイト」「ミックスイン」については後により詳細に説明する機会を設ける。

最後の、

```scala
println("Hello Scala")
```

この記述は、`Hello Scala` という文字列をコンソールに出力して、改行する処理の宣言となる。

println (プリントライン)関数は、引数に渡された文字列 `"Hello Scala"` をコンソールに出力して改行を行う。
Scala では文字列は `"` (ダブルクォーテーション) で囲うことで記述する。

つまりこの記述全体を合わせると、Main というアプリケーションが作られ、そのアプリケーションが実行されるとコンソールに `Hello Scala` と出力されて改行されるということになる。

# 【実習】sbt を立ち上げたままでの実行

再度この `Main.scala` を実行してみよう。

PowerShell またはTerminalで、`scala-study` フォルダに移動し、

```:Terminal
$ sbt
```

以上のコマンドを実行しよう。
これで sbt が立ち上がる。
sbt は実行よりも起動に時間がかかるため、基本的には立ち上げたまま利用する。

```:Terminal
[info] started sbt server
sbt:scala-study> 
```

以上のように表示されれば、sbt が実行を待ち受けている状態になる。

次に、

```:Terminal
sbt:scala-study> run
```

以上を実行して、このフォルダにある `Main.scala` を実行しよう。

```:Terminal
sbt:scala-study> run
[info] running Main 
Hello Scala
[success] Total time: 1 s, completed 2021/07/25 15:54:35
```

このように表示されれば成功。
`sbt run` という風に sbt を立ち上げずに実行しても同じ結果となる。

今度は `"Hello Scala"` の部分を `"アプリケーションを実行しました"` に変更してみよう。
その後 `run` を実行してみよう。

```scala:Main.scala
object Main extends App { println("アプリケーションを実行しました") }
```

以上のような記述となる。
その後 `run` を実行して

```:Terminal
sbt:scala-study> run
[info] compiling 1 Scala source to /Users/glaciermelt/workspace/scala-study/target/scala-2.12/classes ...
[info] running Main 
アプリケーションを実行しました
[success] Total time: 4 s, completed 2021/07/25 15:57:38
```

以上のように表示されれば成功。

```:Terminal
sbt:scala-study> exit
[info] shutting down sbt server
```

`exit` を入力して sbt を終了させよう。

# 【実習】距離を計算するプログラムの実装

次に Scala のプログラミングに慣れるため、いくつか簡単な数学の問題を解いてみる。

ここではまず **「時速のキロメートル」と「走った分数」から「走った距離」を計算する** プログラムを書いてみよう。

`scala-study` フォルダの中に、`Distance.scala` というファイルを作成して、VS Code で編集してみよう。

まずは `Distance.scala` を蚊のように記述しよう。

```scala:Distance.scala
object Distance extends App {
  val kilometersPerHours = 11
  val minutes = 100
  val distance = kilometersPerHours * minutes / 60
  println(s"走った距離は${distance}キロメートルです")
}
```

# 【講義】距離を計算するプログラムの解説

記述できたコードを解説する。
`val` は、Scala における定数宣言。
JavaScript の ES6 における、`const` と似た性質を持っている。
また、再代入可能なものに `var` というものがあるが、可読性・保守性の観点から基本使うことは良しとされない。

```scala
  val kilometersPerHours = 11
  val minutes = 100
```

は、速度が毎時 11 km 、走った時間が 100 min であることを宣言している。
そして、

```scala
  val distance = kilometersPerHours * minutes / 60
```

以上では、「速さ」かける「時間」を行なっている。
分数で時間が渡されているため、時間に直すために 60 で割ってある。

```scala
println(s"走った距離は${distance}キロメートルです")
```

これは、Scala における文字列の中に変数を利用する記述の仕方。
ES6 では \` (バッククオート) を利用してこの String interpolation の機能を実現していたが、Scala では、文字列のリテラルの前に `s` を記述することで、`${変数}` で変数を文字列の中で読み出すことができる。

では実行してみよう。

# 【実習】距離を計算するプログラムの実行

```:Terminal
$ sbt
```

を実行して sbt を立ち上げた後、

```:Terminal
> run
```

を実行してみよう。
すると、

```:Terminal
sbt:scala-study> run
[info] compiling 1 Scala source to /Users/glaciermelt/workspace/scala-study/target/scala-2.12/classes ...

Multiple main classes detected. Select one to run:
 [1] Distance
 [2] Main

```

以上のように、`Distance` か `Main` のどちらのアプリケーションを実行するかを聞かれる。
ここでは、`1` を入力して Enter を押して、`Distance` を実行させよう。

また、指定したアプリケーションで実行したい際には、

```:Terminal
> runMain Distance
```

という風に実行できる。

実行すると、

```:Terminal
[info] running Distance 
走った距離は18キロメートルです
[success] Total time: 0 s, completed 2021/07/25 16:24:29
```

と表示されてしまうだろう。
本来は、11 かける 100 ÷ 60 なので 18.333... のような答えが表示されるはずだが、小数点以下が省略されてしまった。

これは、Scala における割り算 / 演算子による計算は、余りを無視するため。

これを回避するために、予めすべての値を浮動小数点数で与えておく必要がある。
そのように直してみよう。

```scala:Distance.scala
object Distance extends App {
  val kilometersPerHours = 11.0
  val minutes = 100.0
  val distance = kilometersPerHours * minutes / 60.0
  println(s"走った距離は${distance}キロメートルです")
}
```

以上のように記述することで、割り算も浮動小数点数の計算となる。

```:Terminal
> runMain Distance
```

編集の後、以上のコマンドで実行すると、

```:Terminal
[info] running Distance 
走った距離は18.333333333333332キロメートルです
[success] Total time: 1 s, completed 2021/07/25 16:28:37
```

以上のように表示されて、Scala の持つ浮動小数点数の最大桁数に合わせて答えが表示されただろう。
Scala ではこのような計算ができる。

※ ここでは余り（小数以下の部分）に誤差が生じている。
この理由については次で説明する。

# 【講義】Scala における数値の表現と計算

Scala では、数値は整数と浮動小数点数で異なるデータの持ち方をしている。
整数値は [Int](https://www.scala-lang.org/files/archive/api/2.12.6/scala/Int.html) 型、浮動小数点数は [Double](https://www.scala-lang.org/files/archive/api/2.12.6/scala/Double.html) 型というデータで表現される。

プログラミングにおけるデータは、すべて 0 と 1 で表現される2進数で表されている。
数値も同様。

2 進数では、1 は 1 、2 は 10 、3 は 11 、 4 は 100 のように表現される。

Int は、内部的には 4 バイト (32 ビット) のデータで、 `-2147483648` から `2147483647` までの整数を表現可能。
1 ビットが 2進数における一桁分の情報を表している。
そして 8 ビットは 1 バイトに対応する。
Int は、32 ビットのうちの 1 ビットを符号部に、残りの 31 ビットを整数部に割り当てている。
符号部は、正の数か負の数かを表す。

Double は、内部的には 8 バイト (64 ビット) のデータで、`-1.7976931348623157E308` から `1.7976931348623157E308` までの浮動小数点数を表現可能。
`E308` は 10 の 308 乗を表す。
64 ビットのうちの 1 ビットを符号部に、11 ビットを指数部に、52 ビットを仮数部に割り当てている。
指数部は 10 の乗数、仮数部は指数をかける数になる部分を表す。
もちろん、Double は仮数部の大きさで精度が決まるため、特定の計算では誤差を作ってしまう。

`sbt console` にて REPL を立ち上げて、

```
0.1 * 0.1
```

以上の 0.1 かける 0.1 の答えが、

```
Double = 0.010000000000000002
```

このようになり、小さな誤差が生まれてしまう。
距離の計算で余りに誤差が生じていたのは結果が Double 型であるため、同じ原理。

なお、Int と Double 以外にもいくつかバイトサイズの異なる数値の型が用意されているので、以下にまとめておく。

| 値型 | 範囲 | 記述例 |
|:-:|:-:|:-:|
| Byte | 1 バイトの符号付き整数 | 1.toByte |
| Short | 2 バイトの符号付き整数 | 1.toShort |
| Int | 4 バイトの符号付き整数 | 1 |
| Long | 8 バイトの符号付き整数 | 1L |
| Float | 4 バイトの浮動小数点数 | 1.0F |
| Double | 8 バイトの浮動小数点数 | 1.0 |

実際に記述例を REPL にて実行してみよう。

また隠すうちに対して、`toByte` , `toShort` , `toInt` , `toLong` , `toFloat` , `toDouble` というメソッドを呼び出すことで、各型への変換ができる。

なお算術演算子も利用可能で、JavaScript や Java 、 C 言語などと同じ。
算術演算子は以下のもの。

| 計算 | 演算子 | 記述例 |
|:-:|:-:|:-:|
| 足し算 | + | 1 + 1 |
| 引き算 | - | 10 - 7 |
| 掛け算 | * | 2 * 2 |
| 割り算 | / | 1 / 3 |
| 割り算の余り | % | 13 % 7 |

実際に記述例を REPL にて実行してみよう。
ただし割り算は整数値同士の割り算の場合、小数点以下は表現されないため注意が必要。

# 【実習】階乗を計算するプログラムの実装

簡単な計算ができたところで、今度は会場を計算してみよう。
会場の計算には、再帰という関数の定義の中で関数を呼び出すというテクニックが利用できる。

階乗とは、1 からその数値までのすべての数を掛け合わせた値。
ここでは、1000 の階乗を計算してみよう。

`scala-study` フォルダの中に、`Factorial.scala` というファイルを作成して、VS Code で編集してみよう。

```scala:Factorial.scala
import scala.math.BigInt

object Factorial extends App {
  def factorial(i: BigInt): BigInt = if (i == 0) 1 else i * factorial(i - 1)

  println(factorial(1000))
}
```

以上のように記述する。
sbt を起動して、

```:Terminal
> runMain Factorial
```

で実行してみよう。

```:Terminal
[info] compiling 1 Scala source to /Users/glaciermelt/workspace/scala-study/target/scala-2.12/classes ...
[info] running Factorial 
40238726007709377354370243392300398571937486...
[success] Total time: 4 s, completed 2021/07/25 17:09:16
```

以上が表示されれば、実装がうまくいっている。

# 【講義】階乗を計算するプログラムの解説

このコードを解説していく。

```scala
import scala.math.BigInt
```

これは、BigInt という任意精度整数の型を読み込んでいる。
import 文は、このファイルで定義されていない型や関数を、このファイルで利用できるようにする宣言。
Scala において通常扱われる整数 Int は、内部的には 32 bit で表現され、最大値は、 `-2147483648` から `2147483647` までの整数しか表現することができない。
そのため、BigInt という Int の上限を超えて、コンピューターのリソースが許す限りの大きな整数を表すことができる型である BigInt を利用する。

```scala
object Factorial extends App {}
```

これは、オブジェクト Factorial の宣言。

```scala
def factorial(i: BigInt): BigInt = if (i == 0) 1 else i * factorial(i - 1)
```

以上は、階乗を表現するための関数。
関数宣言は、`def` という宣言で始め、ここでは、factorial という名前の関数名を定義する。

関数名の後の最初のカッコ `()` には、引数を宣言する。
引数には型を設定でき、

```
i: BigInt
```

以上の記述では、BigInt という型で i という引数を宣言している。
また、関数の最後には、

```scala
def factorial(i: BigInt): BigInt
```

上記の記述のように `: BigInt` が再度書かれているが、これは関数の戻り値が BigInt となるということを表している。

関数の戻り値の型の後には `=` を書き、それ以降は関数自体の処理を表す。

```scala
if (i == 0) 1 else i * factorial(i - 1)
```

これは if 式という。
JavaScript では if 文があったが、if 文と違いこの if 式は、条件式で与えられた真偽値が真であれば if の直後の値を、偽であれば else の直後の値が返される。

Scala において真偽値は、`true` と `false` で表される。
また、論理演算子は以下のようになる。

| 表現 | 入力例 | 結果 |
|:-:|:-:|:-:|
| 1 は 2 以下 | 1 <= 2 | true |
| 1 は 2 以上 | 1 >= 2 | false |
| 1 は 2 より小さい | 1 < 2 | true |
| 1 は 2 より大きい | 1 > 2 | false |
| 1 は 2 と等しい | 1 == 2 | false |
| 1 は 2 と等しくない | 1 != 2 | true |

JavaScript と異なり、`===` が `==` となっていることに注意。

```scala
if (i == 0) 1 else i * factorial(i - 1)
```

以上を踏まえて先程の指揮を解説すると、i が 0 である場合には 1 を値として返し、そうでない場合には i かける i - 1 の階乗の値を返している。

なおこの factorial は再帰関数として宣言されており、0 の階乗は 1 それ以外の場合の整数 i の場合には、i かける i - 1 の階乗である、と定義されている。

このように Scala では階乗を再帰関数で表すことができる。

```scala
println(factorial(1000))
```

以上の実装は 1000 の階乗の値を、コンソールに出力して改行するもの。
これで Scala を使って数学の計算を行う方法がだんだんわかってきたのではないかと思う。

# まとめ

* Scala の数値は、整数と浮動小数点数で別々に表される
* Scala では、整数同士の割り算の結果では、小数点以下の値は無視されてしまう
* 非常に大きな整数を扱う際には、BigInt を利用する

# 挑戦

## 初級

```scala:Terminal
scala> 2147483647 + 1
val res0: Int = -2147483648

scala> 9223372036854775807L + 1
val res2: Long = -9223372036854775808

scala> 1e308 + 1
val res3: Double = 1.0E308

scala> 1 + 0.0000000000000000001
val res4: Double = 1.0

scala> 1 - 1
val res5: Int = 0

scala> 1 - 0.1
val res6: Double = 0.9

scala> 0.1 - 1
val res7: Double = -0.9

scala> 0.1 - 0.1
val res8: Double = 0.0

scala> 0.0000000000000000001 - 1
val res9: Double = -1.0

scala> 0.1 * 0.1
val res10: Double = 0.010000000000000002

scala> 20 * 0.1
val res11: Double = 2.0

scala> 1 / 3
val res12: Int = 0

scala> 1.0 / 3
val res13: Double = 0.3333333333333333

scala> 1 / 3.0
val res14: Double = 0.3333333333333333

scala> 3.0 / 3.0
val res15: Double = 1.0

scala> 1.0 / 10 * 1 / 10
val res16: Double = 0.01

scala> 1 / 10 * 1 / 10.0
val res17: Double = 0.0
```

## 中級

```scala:Terminal
scala> (129512 * 1.08).toInt
val res19: Int = 139872
```

# 上級

```scala:Terminal
...
[error]         at Factorial$.factorial(Factorial.scala:4)
[error] stack trace is suppressed; run last Compile / bgRunMain for the full output
[error] Nonzero exit code: 1
[error] (Compile / runMain) Nonzero exit code: 1
[error] Total time: 5 s, completed 2021/07/25 21:49:27
```

```scala:Factorial.scala
import scala.math.BigInt

object Factorial extends App {
  def factorial(n: BigInt, acc: BigInt): BigInt = if (n == 0) acc else factorial(n - 1, acc * n)

  println(factorial(30000, 1))
}
```

```scala:Terminal
...
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
[success] Total time: 2 s, completed 2021/07/25 22:06:46
```