匹配Scala中的多个案例类


100

我正在对某些案例类进行匹配,并希望以相同的方式处理两个案例。像这样:

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的定义中删除参数,但是我该如何与参数匹配呢?

Answers:


145

看起来您并不在乎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

4
Scala不允许原因“ case A(aString)| cas​​e B(aString)=> println(aString)”吗?似乎只要A和B的aString类型相同,就应该允许它。您的最后一个示例似乎最好不要重复B和C案例。
詹姆斯·摩尔

37
我再去找你 我认为,case A(x) | B(x) => println(x)允许将xA(x)和B(x)产生的类型系统的类型设置为上限的情况会很好。
米奇·布列文(Mitch Blevins)

1
@MitchBlevins:您可以投票给issue.scala-lang.org/browse/SUGGEST-25(允许以其他方式绑定变量)
Erik Kaplun

2
对于那些想知道@符号在这里做什么的人:scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
SilentDirge,2016年

9

如果案例类之间有一些共性,我可以通过两种方法来实现目标。第一种是让案例类扩展声明共同性的特征,第二种是使用结构化类型,从而无需扩展您的案例类。

 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.