스칼라의 여러 케이스 클래스 일치


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의 정의에서 매개 변수를 제거하여 작동하도록 할 수 있지만 매개 변수와 어떻게 일치시킬 수 있습니까?

답변:


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 2009

4
스칼라가 "case A (aString) | case B (aString) => println (aString)"을 허용하지 않는 이유가 있습니까? aString 유형이 A와 B 모두에 대해 동일한 한 허용되어야합니다. 마지막 예는 B와 C 케이스를 복제하지 않는 것이 더 나을 것 같습니다.
James Moore

37
한 번 더 가겠습니다. A (x) 및 B (x)가 생성하는 유형 시스템에서 유형이 상한으로 설정된 case A(x) | B(x) => println(x)곳에 허용되는 것이 좋을 것이라고 생각합니다 x.
Mitch Blevins 2011

1
@MitchBlevins : 당신이 투표 할 수 issues.scala-lang.org/browse/SUGGEST-25 (변수가 다른 패턴으로 바인딩 수)
에릭 Kaplun

2
@ 기호가 거기에서 무엇을하는지 궁금한 사람들을 위해 : scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
SilentDirge

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가 바인딩되지만 어느 쪽을 사용할지 알 수 없으므로 사용할 항목을 결정하려면 추가 선택 논리가 필요합니다 (옵션 [문자열]에 바인딩 된 경우 문자열). 따라서 이것에 대해 얻은 것은 없습니다.

  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

당신이 올바른지. 값이나 제시된 유형이 아닌 유형에만 관심이 있다면 분리형 유형 기반 일치가 의미 있고 사용 가능합니다.
Randall Schulz 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.