Ölçeklendirmede birden çok vaka sınıfını eşleştirin


100

Bazı vaka sınıflarına göre eşleştirme yapıyorum ve iki vakayı aynı şekilde ele almak istiyorum. Bunun gibi bir şey:

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"
  }
}

Ama bunu yaptığımda şu hatayı alıyorum:

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

Çalışmasını sağlayabilirim, B ve C tanımlarından parametreleri kaldırırım ama parametrelerle nasıl eşleşebilirim?

Yanıtlar:


145

Görünüşe göre String parametrelerinin değerlerini önemsemiyorsunuz ve B ve C'yi aynı şekilde ele almak istiyorsunuz, bu yüzden:

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

Gerekirse, mecbur kalırsanız, parametreyi çıkarmanız ve bunları aynı kod bloğunda ele almanız gerekiyorsa, şunları yapabilirsiniz:

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"
  }
}

Bunu bir yöntem haline getirmenin çok daha temiz olacağını düşünmeme rağmen:

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"
  }
}

Örneğim bunu göstermese de bu parametrelere ihtiyacım var. Görünüşe göre bir nesne kullanmam gerekecek. Teşekkürler!
timdisney

4
Scala'nın "case A (aString) | case B (aString) => println (aString)" 'e izin vermemesinin bir nedeni var mı? AString türü hem A hem de B için aynı olduğu sürece, buna izin verilmelidir. Son örneğiniz B ve C davalarını kopyalamamak daha iyi olacak gibi görünüyor.
James Moore

37
Seni bir daha ileri götüreceğim A (x) ve B (x) ürettiği her ne olursa olsun, türünün tür sisteminde üst sınıra ayarlandığı case A(x) | B(x) => println(x)yerde izin verilmesinin güzel olacağını düşünüyorum x.
Mitch Blevins

1
@MitchBlevins: issues.scala-lang.org/browse/SUGGEST-25 (alternatif düzende değişken bağlamaya izin ver )
Erik Kaplun

2
@ Sembolünün orada ne yaptığını merak edenler için: scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
SilentDirge

9

Vaka sınıfları arasında bazı ortak yönleriniz varsa, peşinde olduğunuz şeyi başarmanın birkaç yolu var. Birincisi, vaka sınıflarının ortaklığı bildiren bir özelliği genişletmesi, ikincisi vaka sınıflarınızı genişletme ihtiyacını ortadan kaldıran yapısal bir tür kullanmaktır.

 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))
   }
 }

Yapısal tip yöntemi, şu anda nasıl ortadan kaldıracağımı bilmediğim, silme konusunda bir uyarı oluşturur.


6

Pek mantıklı değil, değil mi? B ve C birbirini dışlar, bu nedenle ya sb ya da sc sınırlanır, ancak hangisini kullanacağınıza karar vermek için daha fazla seçim mantığına ihtiyacınız vardır (bir Seçenek [Dize] 'ye bağlı olmadıkları için, dizi). Yani bunun üzerinden kazanılmış hiçbir şey yok:

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

Veya bu:

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

Ya B'nin mi yoksa C'nin mi eşleştiği umurunuzda değilse? Aşağıdaki kodu söyleyin: args match { case Array("-x", hostArg) => (hostArg, true); case Array(hostArg, "-x") => (hostArg, true) }Ancak, bunun yaygın bir durum olmadığını ve yerel bir yöntem oluşturmanın bir alternatif olduğunu görüyorum. Bununla birlikte, alternatif uygunsa, vaka alternatiflerine sahip olmanın pek bir anlamı yoktur. Aslında, bazı makine öğrenimi lehçelerinde benzer bir özelliğe sahipsiniz ve her değişken her iki alternatifte de aynı türle bağlandığı sürece (IIRC) değişkenleri bağlayabilirsiniz.
Blaisorblade

Haklısın. Değerleri veya hangi türün sunulduğunu değil, yalnızca türleri önemsiyorsanız, ayırıcı tür tabanlı eşleşme anlamlı ve kullanılabilirdir.
Randall Schulz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.