Scala入門 (1) -式と値-

[Scala入門目次](http://qiita.com/ababup1192/items/289480485e7f1de18d9e) へ

今回のエッセンスは、```式は評価すると結果値を生成する```です。とても当たり前のように思えますが、Scalaではあらゆるところで、この考えが浸透しています。

# 式とブロック

式の例を見ていきましょう。

```scala
$ scala

// 真っ先に思い浮かべる式
scala> 1 + 1
res1: Int = 2

// 単一の値も式
scala> 1
res2: Int = 1

// よくある四則演算
scala> val x = (1 + 2) * 3 - 4 / 2
x: Int = 7

// 変数も式
scala> x
res3: Int = 7

// メソッド呼び出しも式
scala> x.toLong
res4: Long = 7
```

少し特殊な例を紹介しましょう。printlnなどの一見、値を返さなそうなメソッドがあります。しかしScalaではメソッド呼び出しは、式として判別されるため、意味のある結果を返さない*Unit型*として判断されます。

```scala
// これは、値を返しているのではなく、標準出力。
> println("hello")
hello

// :t を式の前に付けると結果型を調べる事ができる。 
> :t println("hello")
Unit
```

中括弧で囲まれた1つ以上の式を **ブロック(式)** と呼びます。ブロックを評価すると、その中の**全ての式と宣言が順に処理され、最後の式の結果値がブロック自身の結果値として返されます**。

```scala
> :paste
// Entering paste mode (ctrl-D to finish)

{
  val x = 1 + 1
  val y = 2 + 2
  x + y
}

// Exiting paste mode, now interpreting.

res4: Int = 6
```

式は評価され結果値を生成する。とても単純な考えですが、Scalaでは多くのところでこの考えが使われています。

# 制御構造と式

制御構造も式です。構文自体は新しいですが、式もしくは、ブロックを持った構造と考えれば単純な応用となります。制御構造は式を受け取り、自身も式として結果値を生成します。*if式*を見てみましょう。

```scala
> val x = 1
res: Int = 1

// 条件を判断して、"even"もしくは、"odd"という式を評価して結果値を生成する。
> if(x % 2 == 0) "even" else "odd"
res: String = "odd"

> :paste

// 式がブロック式に変わっただけ。最後に評価された式が結果値となる。
if(x % 2 == 0) {
  "even"
} else {
  "odd"
}

res: String = odd
```

続いて```for式```を見ていきましょう。少し変わった形ですが、ifと考え方は同じです。Listから取り出された値(x) と式の組み合わせに過ぎません。この例は、*println*を使ったので値を返さなかったのでしょうか？

```scala
> for(x <- List(1, 2, 3)) println(x)
1
2
3
```

このような形で記述しても、値を返すことはありません。やっていることは、Javaの*forEach*と同様になります。そのため、Unit型が返ることになります。

```scala
> :t for(x <- List(1, 2, 3)){ x * 2 }
Unit
```

forが値を返すのは、以下のようなケースです。*yield*キーワードを利用してみましょう。このとき何故このような結果になるかは言及はしません。forは式を受け取り、自身も式として結果値を生成するということに意識を集中しましょう。

```scala
> for(x <- List(1, 2, 3)) yield x * 2
res: List[Int] = List(2, 4, 6)
```

*case式*を見てみましょう。現時点では、値を生成する*switch-case*という認識で構いません。


```scala
> val x = 3
x: Int = 3
> :paste
 
x match {
  case 1 => "1"
  case 2 => "2"
  case 3 => "3"
  case _ => "Other numbers"
}

res: String = 3
```

最後に制御構造ではありませんが、scalaのメソッド定義を見てみましょう。メソッドは、**0個以上の引数と式の組**という見方ができます。式は、単体の式かブロックの形で```=```記号によって紐付けられています。そのため、メソッドも呼び出され評価されると結果値を生成します。

```scala
> def foo(x: Int): Int = x * 2
foo: (x: Int)Int
> foo(3)
res: Int = 6

> def bar(x: Int): String = {
   if(x == 1) "1"
   else "Other numbers"
}
> bar(3)
res: String = Other numbers
```

いろんな制御構造が式(ブロック)を受け取り、**自身も結果値を返す式**ということがわかりました。それでは、これがプログラミングにどのような影響を及ぼすかを見ていきましょう。


# 単体テスト

REPLで複数行記述するのも限界に近づいてきたので、sbtとエディタ・IDEを利用してScalaファイルを書いていきましょう。ついでに今まで書いてきた式は結果値を生成するので、標準出力をしながらではなくテストコードを書きながら学習を進めていきましょう(これを機にテストコードを書く習慣が身につくと一石二鳥ですね)。

```
$ sbt new scala/scala-seed.g8
Minimum Scala build.

name [My Something Project]: expression-and-value

Template applied in ./expression-and-value

$ cd expression-and-value
```

テンプレートには、あらかじめ*ScalaTest*というテストフレームワークが用意されています。テストコードを書くため、以下の2つのファイルを用意してみましょう。(初めに用意されているexampleパッケージは消してしまっても構いません。)



```scala:src/main/scala/expression/Expression.scala
package expression

object Expression {
  val one = 1
}
```


```scala:src/test/scala/expression/ExpressionSpec.scala
package expression

import org.scalatest.FunSuite

class ExpressionSpec extends FunSuite {
  test("Expression::one should return 1") {
    assert(Expression.one == 1)
  }
}
```

2つのファイルが用意できたら、sbtで*test*コマンドを実行してみましょう。緑色の文字で以下のように表示されれば成功です。

```
$ sbt
> test
[info] Compiling 1 Scala source to /Users/abab/Desktop/expression-and-value/target/scala-2.12/classes...
[info] Compiling 1 Scala source to /Users/abab/Desktop/expression-and-value/target/scala-2.12/test-classes...
[info] ExpressionSpec:
[info] - Function one should return 1
[info] Run completed in 253 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 6 s, completed 2017/02/22 9:36:07
```

今まで書いてきた式も同様にテストに掛けてみましょう。



```scala:src/main/scala/expression/Expression.scala
package expression

object Expression {
  val one = 1

  def evenOdd(x: Int): String = {
    if(x % 2 == 0) "even"
    else           "odd"
  }

  def multiList(mul: Int): List[Int] = {
    for(x <- List(1, 2, 3, 4, 5)) yield x * mul
  }

  def oneTwoThree(x: Int): String = {
    x match {
      case 1 => "1"
      case 2 => "2"
      case 3 => "3"
      case _ => "Other numbers"
    }
  }
  
}
```

テストコードは以下のようになります。

```scala:src/test/scala/expression/ExpressionSpec.scala
package expression

import org.scalatest.FunSuite

class ExpressionSpec extends FunSuite {
  test("Function one should return 1") {
    assert(Expression.one == 1)
  }

  test("""Expression::evenOdd should return "even" given 2""") {
    assert(Expression.evenOdd(2) == "even")
  }

  test("""Expression::evenOdd should return "odd" given 3""") {
    assert(Expression.evenOdd(3) == "odd")
  }

  test("Expression::multiList should return list of multiples of 2 given 2") {
    assert(Expression.multiList(2) == List(2, 4, 6, 8, 10))
  }

  test("""Expression::oneTwoThree should return "1" given 1""") {
    assert(Expression.oneTwoThree(1) == "1")
  }
}
```

さらに追加情報ですがsbtの対話環境では、consoleコマンドでREPLを開き自身の定義したメソッドをimportして試すことができます。デバッグの際にお使いください。

```scala
$ sbt
> console
scala> import expression.Expression
scala> Expression.evenOdd(2)
result: String = even
```

今回は単独の式(関数)をテストしているだけですが、**単純な式を組み合わせて複雑な式を作ってテストしていき、安全なプログラムを書くことができる**のは関数型言語の強みです。単純なものからテストをしていく習慣を身に着けていきましょう。

# 文と式

今まで多くのScalaの機能に式の考えが使われていることを見てきました。ここでは、Javaについて見ていきましょう。**Scalaでは式主体**であったのに対して、**Javaでは文主体**です。

あるif文を持つJavaのメソッドを考えてみましょう。Scalaと比べて*return*キーワードがある分、冗長に見えます。

```java
public int foo(int x){
  if(x % 2 == 0){
     return 0;
  } else {
     return 1;
  }
}
```

この様な単純な場合、三項演算子を利用すると短く記述することができます。

```java
public int foo(int x){
  return x % 2 == 0 ? 0 : 1;
}
```

それでは、条件が3つ以上に増えた場合はどうでしょうか？ やはり*return*キーワードが冗長です。

```java
public String bar(int x){
  if(x % 2 == 0){
     return "2";
  } else if(x % 3 == 0) {
     return "3";
  } else {
    return "other";
  }
}
```

それでは三項演算子を使ってみては、どうでしょうか？これは、少し書き方を工夫しても見づらい上にネストしたケースや単純な値ではなく、式(複数行に渡る文)を書きたい場合も難しくなってしまいます。

```java
public String foo(int x){
  return x % 2 == 0 ?
    "2" :
    x % 3 == 0 ?
    "3" :
    "other";
}
```

Javaでは、以下のように書くケースが多いと思われます。~~しかし、以下のコードには間違いが含まれています。コンパイラは*res*に初期値が代入されていなければエラーを起こします。代わりに何を入れるでしょうか？ そうです、*null*です。この手法で書いた場合、else節を書き忘れてしまえば、そのまま*null*が返却されてしまい```NullPointerException```が発生してしまいます。~~

コメントでご指摘頂きました。以下のようなケースの場合、else節までしっかり書かれている場合は、null初期化の必要はありません。else節を書き忘れた場合は、初期化されていない可能性をコンパイラが教えてくれるため、そのときに無理にコンパイルを通そうと*null*を代入すると```NullPointerException```を起こしうるコードができてしまうので気をつけましょう。

```java
public String bar(int x){
  String res; // = null;
  
  if(x % 2 == 0){
     res = "2";
  } else if(x % 3 == 0) {
     res = "3";
  } else { // else節を書き忘れたら・・・？
    res = "other";
  }
  return res;
}
```

Scalaでの例を見てみましょう。構文としては、if-elseで書くことができ、さらに*return*キーワードが無いため、とても簡潔で可読性が高いです。

```scala
def bar(x: Int): String = {
  if(x % 2 == 0){
    "2"
  } else if(x % 3 == 0){
    "3"
  }
}
```

さらにコンパイラはメソッドがStringの値を返すことをチェックします。そのため、コンパイル時に以下のようなエラーになり、とても安全に書くことができます。

```
$ scalac Bar.scala

Bar.scala:5: error: type mismatch;
 found   : Unit
 required: String
    } else if(x % 3 == 0){
           ^
one error found
```

もう一つ例を見てみましょう。Java8から導入された**Stream API**です。言わずもがな関数型言語、おそらくScalaの影響も受けていると思います。

文字列のリストの要素を*map*メソッドで角括弧[]で囲んだ文字に変換し、*StringJoiner*を使い、コロン: 区切りの文字列に変換している例です。ラムダ式は値を返す式であることがわかります。

```java
Stream.of("John", "Alice", "Mike", "Mary").stream().map(name -> 
  "[" + name + "]").
   collect(Collectors.joining(":"));
// [John]:[Alice]:[Mike]:[Mary]
```

しかし、Javaでは、波括弧{}で囲まれたブロックは、文を表すため*return*キーワードが必要になります。そのため式と文の頭の切り替えが必要になります。

```java
(name -> {
    String enclosedName = "[" + name + "]";
    return enclosedName;
  }
)
```

Scalaでは以下のように書きます。Scalaのラムダ式は、その名の通り式を書くためブロックで書いたとしても今まで通り値を返します。その他にも、```List型```が*map*メソッドを備えていたり、*collect*メソッドを使用する必要が無いため非常に簡潔に書くことができます。

```scala
List("John", "Alice", "Mike", "Mary").
  map(name => s"[$name]").
  mkString(":")
```



文主体でプログラムを書いた場合、冗長性・安全性・可読性のバランスを取るのが非常に難しいです。式主体の考え方は、これらの問題を綺麗に解決することができます。

# 演習

コードを書かなければ感覚を掴んだり覚えたりすることは難しい思います。演習問題を用意したので、問いてみましょう。別途資料を参照しなくても、本稿の知識のみで解けるはずです。

- case式でパターン漏れがあるとコンパイル時にどうなるか検証してみましょう。
- src/test/scala/expression/ExpressionSpec.scala で足らないテストケースを埋めてみましょう。
- 1 - N までのFizzBuzz文字列のリストを返す関数(式) *fizzBuzz*を2パターン定義して、テストを書いてみましょう。
  - fizzBuzz(5) == List("1", "2", "fizz", "4", "buzz")
  - ヒント: def, for, if, case

## まとめ

多くの事柄について学んで来ましたが、一貫しているのは、```式は評価すると結果値を生成する```ということだけです。このエッセンスだけで、

- 記述が簡潔になり可読性が上がる
- テストコードが書きやすくなる
- コンパイラが安全なコードを導いてくれる

こんなに多くの利点を得ることができました。Scalaを書く際には、式を強く意識してみましょう。