Scala
memo

Scalaの制御構文(2)

More than 1 year has passed since last update.

はじめに

下記のテキストを通して、基本的な書き方を学びつつ、個人的に気になったことを検証していくメモ代わりです。
https://dwango.github.io/scala_text/basic.html
本当にありがとう、dwango様・・・

Scalaの制御構文

http://qiita.com/tamagokake_gohan/items/ee2ce1157c5f4e646a66
の続き

ざっくりまとめ

  • match式はbreakが無い代わりに | によって複数のOR条件をつけることが可能
  • match式は型の識別ができるがObjectであることや、JVMの制約等があるため使用するときは注意が必要

match式

  • 一般系

    マッチ対象の式 match {
        case パターン1 [if ガード1] => 式1
        case パターン2 [if ガード2] => 式2
        case パターン3 [if ガード3] => 式3
        case ...
        case パターンN => 式N
    }
    
  • 使用方法

    • 通常使用

      scala> val name: String = "taro"
      name: String = taro
      
      scala> name match {
           |     case "taro" => "Male"
           |     case "Jiro" => "Male"
           |     case "Hanako" => "Female"
           | }
      res18: String = Male
      
    • ワイルドカード

      scala> val number1: Int = 1
      number1: Int = 1
      
      scala> val number2: Int = 2
      number2: Int = 2
      
      scala> val number3: Int = 3
      number3: Int = 3
      
      scala> number1 match { case 1 => "one"; case 2 => "two"; case _ => "other"}
      res22: String = one
      
      scala> number2 match { case 1 => "one"; case 2 => "two"; case _ => "other"}
      res23: String = two
      
      scala> number3 match { case 1 => "one"; case 2 => "two"; case _ => "other"}
      res24: String = other
      
    • 型が違う場合

      scala> val number1: Integer = 1
      number1: Integer = 1
      
      scala> number1 match { case 1 => "one"; case 2 => "two"; case _ => "other"}
      <console>:9: error: type mismatch;
       found   : Int(1)
       required: Integer
                    number1 match { case 1 => "one"; case 2 => "two"; case _ => "other"}
                                         ^
      
      • errorとして返されるから注意が必要
    • C言語だとbreakがあるが、Scalaだと無いのでまとめる

      scala> val str: String = "a"
      str: String = a
      
      scala> str match { case "a" => "A"; case "b" => "B"; }
      res25: String = A
      
      scala> str match { case "a" | "b" => "A or B"}
      res26: String = A or B
      
    • パターンマッチで値を取り出す

      scala> val lst = List("A", "B", "C")
      lst: List[String] = List(A, B, C)
      
      scala> lst match { case List("A", b, c) => "A" + b + c; case _ => "other"}
      res27: String = ABC
      
      scala> lst match { case List(a, "B", c) => a + "B" + c; case _ => "other"}
      res28: String = ABC
      
      scala> lst match { case List("A", b, c) if b != "B" =>  "A" + b + c; case _ => "other"}
      res29: String = other
      
      • case文内でif処理を同時にできるのは便利
      • Listの途中であっても、判定可能(どこでも良いみたい)
    • ネストしている場合の取得

      scala> val lst = List(List("N"), List("A", "B", "C"))
      lst: List[List[String]] = List(List(N), List(A, B, C))
      
      scala> lst match { case List(n@List("N"), x) => n + x; case _ => "other"}
      <console>:9: error: type mismatch;
       found   : List[String]
       required: String
                    lst match { case List(n@List("N"), x) => n + x; case _ => "other"}
                                                                 ^
      
      scala> lst match { case List(n@List("N"), x) => print(n) + print(x); case _ => "other"}
      <console>:9: error: type mismatch;
       found   : Unit
       required: String
                    lst match { case List(n@List("N"), x) => print(n) + print(x); case _ => "other"}
                                                                             ^
      
      scala> lst match { case List(n@List("N"), x) => print(n) ; print(x); case _ => "other"}
      List(N)List(A, B, C)res32: Any = ()
      
      • 検索対象は @ で取り出すことが可能
      • ;; はちょっと気持ちが悪い(というより通常一行で書かないか・・・)
      • ついでに print の返り値はUnit型なのでString型の結合はできない
    • | を使ったパターンマッチの場合は値を取り出すことができない

      scala> (List("a"): Any) match { case List(a) | Some(a) => println(a)}
      <console>:8: error: illegal variable in pattern alternative
                    (List("a"): Any) match { case List(a) | Some(a) => println(a)}
                                                       ^
      <console>:8: error: illegal variable in pattern alternative
                    (List("a"): Any) match { case List(a) | Some(a) => println(a)}
                                                                 ^
      
      scala> val str: String = "a"
      str: String = a
      
      scala> str match { case a@"a" | a@"b" => a}
      <console>:9: error: illegal variable in pattern alternative
                    str match { case a@"a" | a@"b" => a}
                                     ^
      <console>:9: error: illegal variable in pattern alternative
                    str match { case a@"a" | a@"b" => a}
                                             ^
      
      • 組み合わせの場合取り出せないのは不便?
        • そもそもそんな書き方をするのがナンセンスってことで弾かれていそう
    • 型によるパターンマッチ

      scala> val target_s: String  = "hoge"
      target_s: String = hoge
      
      scala> target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
      <console>:9: error: object java.lang.String is not a value
                    target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
                                          ^
      <console>:9: error: object java.lang.Integer is not a value
                    target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
      
      • matchのcaseに対して型を直接入れることはできないっぽい
        • 値じゃねーよって怒れる。まぁ、そうかw
      scala> val target_s: String  = "hoge"
      target_s: String = hoge
      
      scala> target_s match { case v:String => "is string"; case v:Integer => "is Integer"; case _ => "other"}
      <console>:9: error: scrutinee is incompatible with pattern type;
       found   : Integer
       required: String
                    target_s match { case v:String => "is string"; case v:Integer => "is Integer"; case _ => "other"}
                                                                          ^
      
      • 対象の式に対して型が違うと怒られる
        • Object型の式でないからかな?
      scala> val target_s: AnyRef  = "hoge"
      target_s: AnyRef = hoge
      
      scala> target_s match { case v:String => "is string"; case v:Integer => "is Integer"; case _ => "other"}
      res44: String = is string
      
      • Object型ならマッチする
      scala> val target_s: AnyRef  = "hoge"
      target_s: AnyRef = hoge
      
      scala> target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
      <console>:9: error: object java.lang.String is not a value
                    target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
                                          ^
      <console>:9: error: object java.lang.Integer is not a value
                    target_s match { case String => "is string"; case Integer => "is Integer"; case _ => "other"}
                                                                      ^
      
      • ちなみに、対象の式がObject型であっても、パターンに対して変数を仕込む必要がある
      scala> val target_s: AnyRef  = "hoge"
      target_s: AnyRef = hoge
      
      scala> target_s match { case v:String => v + " is string"; case v:Integer => v + " is Integer"; case _ => "other"}
      res47: String = hoge is string
      
      • match内部で変数を使用することが可能
      scala> val target_s: AnyRef  = "hoge"
      target_s: AnyRef = hoge
      
      scala> target_s match { case v@String => v + " is string"; case v@Integer => v + " is Integer"; case _ => "other"}
      <console>:9: error: object java.lang.String is not a value
                    target_s match { case v@String => v + " is string"; case v@Integer => v + " is Integer"; case _ => "other"}
                                            ^
      <console>:9: error: object java.lang.Integer is not a value
                    target_s match { case v@String => v + " is string"; case v@Integer => v + " is Integer"; case _ => "other"}
                                                                               ^
      
      scala> target_s match { case c@v:String => v + " is string"; case c@v:Integer => v + " is Integer"; case _ => "other"}
      <console>:1: error: '=>' expected but ':' found.
             target_s match { case c@v:String => v + " is string"; case c@v:Integer => v + " is Integer"; case _ => "other"}
                                      ^
      <console>:1: error: '=>' expected but ':' found.
             target_s match { case c@v:String => v + " is string"; case c@v:Integer => v + " is Integer"; case _ => "other"}
                                                                           ^
      
      • 遊び心で @: を組み合わせて見たが、エラーですねw
    • JVMの制約による型のパターンマッチの落とし穴

      scala> val obj: Any = List("a")
      obj: Any = List(a)
      
      scala> obj match {
           |     case v: List[Int]    => println("List[Int]")
           |     case v: List[String] => println("List[String]")
           | }
      <console>:10: warning: non-variable type argument Int in type pattern List[Int] is unchecked since it is eliminated by erasure
                        case v: List[Int]    => println("List[Int]")
                                ^
      <console>:11: warning: non-variable type argument String in type pattern List[String] is unchecked since it is eliminated by erasure
                        case v: List[String] => println("List[String]")
                                ^
      <console>:11: warning: unreachable code
                        case v: List[String] => println("List[String]")
                                                       ^
      List[Int]
      
      • 型変数を使った場合、正しくパターンマッチが実施されない

        • 型変数を含む型のパターンマッチは、ワイルドカードパターンを使うとよいとのこと
        obj match {
          case v: List[_] => println("List[_]")
        }