จับคู่คลาสเคสต่างๆใน 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 แต่ฉันจะจับคู่กับพารามิเตอร์ได้อย่างไร

คำตอบ:


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) | case B (aString) => println (aString)"? ดูเหมือนว่าตราบใดที่ประเภทของ aString เหมือนกันสำหรับทั้ง A และ B ก็ควรได้รับอนุญาต ตัวอย่างสุดท้ายของคุณดูเหมือนว่าจะเป็นการดีกว่าถ้าไม่ทำซ้ำกรณี B และ C
James Moore

37
ฉันจะไปต่อให้คุณ ฉันคิดว่ามันจะเป็นการดีที่ได้case A(x) | B(x) => println(x)รับอนุญาตเมื่อประเภทของxถูกตั้งค่าเป็นขอบเขตบนในระบบประเภทของสิ่งที่สร้าง A (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

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

คุณถูก. หากคุณสนใจเฉพาะประเภทไม่ใช่ค่าหรือประเภทที่ถูกนำเสนอการจับคู่ตามประเภทที่ไม่ต่อเนื่องจะมีความหมายและพร้อมใช้งาน
Randall Schulz
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.