Cocokkan beberapa kelas kasus dalam skala


100

Saya melakukan pencocokan terhadap beberapa kelas kasus dan ingin menangani dua kasus dengan cara yang sama. Sesuatu seperti ini:

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

Tetapi ketika saya melakukan ini saya mendapatkan kesalahan:

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

Saya bisa membuatnya berfungsi dengan menghapus parameter dari definisi B dan C tetapi bagaimana saya bisa mencocokkan dengan parameter?

Jawaban:


145

Sepertinya Anda tidak peduli dengan nilai parameter String, dan ingin memperlakukan B dan C sama, jadi:

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

Jika Anda harus, harus, mengekstrak parameter dan memperlakukannya dalam blok kode yang sama, Anda dapat:

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

Meskipun saya merasa akan jauh lebih bersih untuk memfaktorkannya menjadi sebuah metode:

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

Meskipun contoh saya tidak menunjukkannya, saya membutuhkan params itu. Sepertinya saya hanya perlu menggunakan sebuah objek. Terima kasih!
timdisney

4
Apakah ada alasan mengapa scala tidak mengizinkan "kasus A (aString) | kasus B (aString) => println (aString)"? Sepertinya selama jenis aString identik untuk A dan B, itu harus diizinkan. Contoh terakhir Anda sepertinya akan lebih baik tidak menduplikasi casing B dan C.
James Moore

37
Aku akan pergi lebih jauh. Saya pikir akan menyenangkan untuk case A(x) | B(x) => println(x)diizinkan di mana tipe xdiatur ke batas atas dalam sistem tipe apa pun yang dihasilkan A (x) dan B (x).
Mitch Blevins

1
@MitchBlevins: Anda dapat memberikan suara untuk issues.scala-lang.org/browse/SUGGEST-25 (memungkinkan pengikatan variabel dalam pola alternatif)
Erik Kaplun

2
Bagi mereka yang bertanya-tanya apa sih yang dilakukan simbol @ di sana: scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html
SilentDirge

9

Ada beberapa cara yang dapat saya lihat untuk mencapai apa yang Anda inginkan, jika Anda memiliki kesamaan antara kelas kasus. Yang pertama adalah membuat kelas kasus memperluas sifat yang menyatakan kesamaan, yang kedua adalah menggunakan tipe struktural yang menghilangkan kebutuhan untuk memperluas kelas kasus Anda.

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

Metode tipe struktural menghasilkan peringatan tentang penghapusan yang, saat ini saya tidak yakin bagaimana cara menghilangkannya.


6

Yah, itu tidak masuk akal, bukan? B dan C saling eksklusif, jadi sb atau sc terikat, tetapi Anda tidak tahu yang mana, jadi Anda memerlukan logika seleksi lebih lanjut untuk memutuskan mana yang akan digunakan (mengingat bahwa keduanya terikat ke Opsi [String], bukan Sebuah benang). Jadi tidak ada yang didapat dari ini:

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

Atau ini:

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

Bagaimana jika Anda tidak peduli apakah B atau C cocok? Katakanlah dalam kode berikut: args match { case Array("-x", hostArg) => (hostArg, true); case Array(hostArg, "-x") => (hostArg, true) }Namun, saya melihat itu bukan kasus umum dan membuat metode lokal adalah alternatif. Namun, jika alternatifnya nyaman, maka tidak ada gunanya memiliki alternatif case. Sebenarnya, dalam beberapa dialek ML Anda memiliki fitur yang serupa dan Anda masih dapat mengikat variabel, selama (IIRC) karena setiap variabel terikat dengan jenis yang sama pada kedua alternatif.
Blaisorblade

Anda benar. Jika Anda hanya peduli tentang jenis dan bukan nilai maupun jenis yang disajikan, kecocokan berbasis jenis disjungtif bermakna dan tersedia.
Randall Schulz
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.