【问题标题】:Match multiple cases classes in scala在scala中匹配多个案例类
【发布时间】:2023-03-11 05:19:01
【问题描述】:

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

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 的定义中删除参数来使其正常工作,但是如何与参数匹配?

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    看起来你不关心 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"
      }
    }
    

    【讨论】:

    • 虽然我的例子没有显示出来,但我需要这些参数。看起来我只需要使用一个对象。谢谢!
    • scala 是否有理由不允许“case A(aString) | cas​​e B(aString) => println(aString)”?似乎只要 A 和 B 的 aString 类型相同,就应该允许。您的最后一个示例似乎最好不要复制 B 和 C 案例。
    • 我会更进一步。我认为如果 x 的类型设置为 A(x) 和 B(x) 产生的类型系统中的上限,则允许 case A(x) | B(x) => println(x) 会很好。
    • @MitchBlevins:你可以投票给issues.scala-lang.org/browse/SUGGEST-25(允许以替代模式绑定变量)
    • 对于那些想知道@符号在那里做什么的人:scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
    【解决方案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))
       }
     }
    

    结构类型方法会生成有关擦除的警告,目前我不确定如何消除。

    【讨论】:

      【解决方案3】:

      嗯,这真的没有意义,不是吗? 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) 每个变量在两个备选方案上都与相同的类型绑定。
      • 你是对的。如果您只关心类型而不关心值或呈现的类型,则基于类型的析取匹配是有意义且可用的。
      猜你喜欢
      • 2020-10-04
      • 1970-01-01
      • 1970-01-01
      • 2019-03-09
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多