Scalaで複数のCasesクラスを一致させる


99

一部のケースクラスに対してマッチングを行っていますが、2つのケースを同じ方法で処理したいと考えています。このようなもの:

abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo


def matcher(l: Foo): String = {
  l match {
    case A() => "A"
    case B(sb) | C(sc) => "B"
    case _ => "default"
  }
}

しかし、これを行うとエラーが発生します:

(fragment of test.scala):10: error: illegal variable in pattern alternative
    case B(sb) | C(sc) => "B"

BとCの定義からパラメーターを削除して機能させることができますが、パラメーターとどのように一致させることができますか?

回答:


144

Stringパラメータの値は気にせず、BとCを同じように扱いたいので、次のようにします。

def matcher(l: Foo): String = {
  l match {
    case A() => "A"
    case B(_) | C(_) => "B"
    case _ => "default"
  }
}

パラメータを抽出し、同じコードブロックで処理する必要がある場合は、次のことができます。

def matcher(l: Foo): String = {
  l match {
    case A() => "A"
    case bOrC @ (B(_) | C(_)) => {
      val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
      "B(" + s + ")"
    }
    case _ => "default"
  }
}

それをメソッドに分解するほうがはるかにクリーンだと思いますが:

def doB(s: String) = { "B(" + s + ")" }

def matcher(l: Foo): String = {
  l match {
    case A() => "A"
    case B(s) => doB(s)
    case C(s) => doB(s)
    case _ => "default"
  }
}

私の例ではそれを示していませんが、これらのパラメーターが必要です。オブジェクトを使用する必要があるようです。ありがとう!
timdisney 2009

4
scalaが「case A(aString)| cas​​e B(aString)=> println(aString)」を許可しない理由はありますか?aStringのタイプがAとBの両方で同じである限り、許可する必要があります。あなたの最後の例は、BとCのケースを複製しないほうがいいようです。
James Moore

36
私はあなたにさらに行きます。A(x)とB(x)が生成するすべての型システムの型が上限に設定されているcase A(x) | B(x) => println(x)場合に許可されているとよいと思いますx
ミッチブレビン

1
@MitchBlevins:あなたがのために投票することができissues.scala-lang.org/browse/SUGGEST-25(変数が代替パターンで結合を可能にする)
エリックKaplun

1
@記号が一体何をしているのか不思議に思う人のために:scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
SilentDirge

9

ケースクラス間にいくつかの共通点がある場合、私はあなたが何をしているのかを達成するために私が見ることができるいくつかの方法があります。1つ目は、ケースクラスに共通性を宣言する特性を拡張させることであり、2つ目は、ケースクラスを拡張する必要性を排除する構造型を使用することです。

 object MuliCase {
   abstract class Foo
   case object A extends Foo

   trait SupportsS {val s: String}

   type Stype = Foo {val s: String}

   case class B(s:String) extends Foo
   case class C(s:String) extends Foo

   case class D(s:String) extends Foo with SupportsS
   case class E(s:String) extends Foo with SupportsS

   def matcher1(l: Foo): String = {
     l match {
       case A        => "A"
       case s: Stype => println(s.s); "B"
       case _        => "default"
     }
   }

   def matcher2(l: Foo): String = {
     l match {
       case A            => "A"
       case s: SupportsS => println(s.s); "B"
       case _            => "default"
     }
   }

   def main(args: Array[String]) {
     val a = A
     val b = B("B's s value")
     val c = C("C's s value")

     println(matcher1(a))
     println(matcher1(b))
     println(matcher1(c))

     val d = D("D's s value")
     val e = E("E's s value")

     println(matcher2(d))
     println(matcher2(e))
   }
 }

構造型の方法では、消去に関する警告が表示されますが、現時点では消去する方法がわかりません。


6

まあ、それは本当に意味がありませんね?BとCは相互に排他的であるため、sbまたはscのどちらかがバインドされますが、どちらが使用されるかはわかりません。したがって、どちらを使用するかを決定するための追加の選択ロジックが必要です(それらがOption [String]にバインドされている場合、文字列)。したがって、これについては何も得られません。

  l match {
    case A() => "A"
    case B(sb) => "B(" + sb + ")"
    case C(sc) => "C(" + sc + ")"
    case _ => "default"
  }

またはこれ:

  l match {
    case A() => "A"
    case _: B => "B"
    case _: C => "C"
    case _ => "default"
  }

BまたはCが一致したかどうかを気にしない場合はどうなりますか?次のコードargs match { case Array("-x", hostArg) => (hostArg, true); case Array(hostArg, "-x") => (hostArg, true) }で言ってください。しかし、それは一般的なケースではなく、ローカルメソッドの作成が代替手段であることがわかります。ただし、代替手段が便利な場合は、ケースの代替手段を用意しても意味がありません。実際、一部のML方言では同様の機能があり、各変数が両方の選択肢で同じ型にバインドされている限り(IIRC)、変数をバインドできます。
Blaisorblade、2011年

あなたは正しいです。タイプのみに関心があり、値も提示されたタイプも気にしていない場合、選言型ベースの一致は意味があり、利用可能です。
Randall Schulz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.