読み解き PiS3: ケースシーケンスの実体は関数リテラルである

A sequence of cases (i.e., alternatives) in curly braces can be used anywhere a function literal can be used.Essentially, a case sequence is a function literal, only more general. Instead of having a single entry point and list of parameters, a case sequence has multiple entry points, each withtheir own list of parameters. Each case is an entry point tothe function, and the parameters are specified with the pattern. Thebody of each entry point is the right-hand side of the case. (Chapter 15. Case Classes and Pattern Matching)

  • 偶数判定の関数リテラル(function literals) を作ってみる。Lambda が返っているので無名関数と言ってもよいのかなと思う(Lisp 好きな私には lambda は懐かしい)
scala> val isEven = (x: Int) => { if((x % 2) == 0) true else false }
isEven: Int => Boolean = $$Lambda$3888/394887728@36f14bca

scala> isEven(3)
res10: Boolean = false

scala> isEven(4)
res11: Boolean = true
  • もうちょっと短くしてこれでも大丈夫だった。
scala> val isEven = (x: Int) => (x % 2) == 0
isEven: Int => Boolean = $$Lambda$4044/817940743@616ae52
  • ケースシーケンス中に関数リテラルを埋め込んでみる。
scala> val isEven: Int => Boolean = {
     |   case x: Int => (x % 2) == 0
     | }
isEven: Int => Boolean = $$Lambda$4043/1380981831@d5d3ee

scala> isEven(10)
res49: Boolean = true

scala> isEven(11)
res50: Boolean = false
  • ケースシーケンスを val にひもづかせる時の値としての関数(function values)の書き方は少し特殊だが以下のようだ。PiS3 に説明の記述が見当たらなかった。|ω・)
val variable_name: argument_type => return_type = { ... body ... } 

 読み解き PiS3: ケースクラスのパターンマッチング(Constructor パターン)

  • case class は abstract class の派生でなくてもいい。コップ本だと Expr の派生クラスとして UnOp や BinOp クラスを定義しているが別に親がいなくてもいい。
  • パターンマッチのブロックの中の e はパターンに束縛される一時変数。「この e はどこから出てきたんだ」と思ってしまった。これは慣れるしかない。

In this example, note that the first three alternatives evaluate to e, a variable that is bound withinthe associated pattern. (Chapter 15. Case Classes and Pattern Matching)

  • パターンは以下の種類がある。
    • Wildcards, Constant, Variable, Constructor, Sequence, Tupple, Typed
  • Expr の例はパターンマッチの Constructor パターンに該当する。自分でも Constructor パターンを試してみる。
case class Room(number: Int, kind: String)

val r1 = Room(1001, "single")
val r2 = Room(2001, "double")

def tellMeRoomKind(r: Room): Unit = {
  r match {
    case Room(n, "single") => println(s"Room $n is a single room.")
    case Room(n, "double") => println(s"Room $n is a double room.")
    case Room(n, _) => println(s"Room $n is unknown-kind room.")
  }
}

tellMeRoomKind(r1)
  • パターンマッチも慣れてくれば「これはこのパターンだな」と認識できて、ぐっと表現の幅が広がりそう。Scala は文脈によっていろいろな意味を持たせられるようだ。慣れていないと簡潔な表現を読み解くのが大変な反面、慣れてしまえばその簡潔さゆえに、読み書きともかなりスピードがあがるはず。 (´͈ ᗨ `͈ )

読み解き PiS3: def の後のイコールのありなし

  • def の後の body の前に = を置かない場合と置く場合の違いが気になっていた。
    • = を置かない場合はメソッド/関数の戻り値は Unit になる。
    • = を置く場合はメソッド/関数の戻り値を任意に指定する。

The equals sign that precedes the body of a function hints that in the functional world view, a function defines an expression that results in a value.The basic structure of a function is illustrated in Figure 2.1. (Chapter 2. First Steps in Scala)

  • 自分で sbt console から試してみる。
scala> def hello(s: String) { println(s"hello, $s.") }

hello: (s: String)Unit

scala> hello("world")
hello, world.
scala> def hello(s: String) = { println(s"hello, $s.") }
hello: (s: String)Unit

scala> hello("world")
hello, world.
scala> def hello(s: String): Unit = { println(s"hello, $s.") }

hello: (s: String)Unit

scala> hello("world")
hello, world.
scala> def hello(s: String): String = { println(s"hello, $s.") }
<console>:11: error: type mismatch;
found : Unit
required: String
def hello(s: String): String = { println(s"hello, $s.") }
^
scala> def hello(s: String): String = { "hello, " + s + "." }
hello: (s: String)String

scala> hello("world")
res5: String = hello, world.
  • 同じ疑問を持っている方が説明してくれていて助かった。( ・∀・)

blog.jessitron.com