2009-12-03 10 views
90

Estoy haciendo coincidir con algunas clases de casos y me gustaría manejar dos de los casos de la misma manera. Algo como esto:Coincide con múltiples clases de casos en scala

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

Pero cuando hago esto me sale el error:

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

puedo conseguir que funcione de que quitar los parámetros de la definición de B y C, pero ¿cómo se puede hacer coincidir con los params?

Respuesta

123

parece que no se preocupan por los valores de los parámetros de cadena, y quiere tratar a B y C de la misma, por lo :

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

Si es necesario, debe, debe extraer los parámetros y tratarlos en el mismo bloque de código, usted podría:

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

Aunque siento que sería mucho más limpio al factor de que fuera en un método:

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

Aunque mi ejemplo no lo muestra, necesito esos parámetros. Parece que tendré que usar un objeto. ¡Gracias! – timdisney

+4

¿Existe algún motivo por el que Scala no permita "caso A (aString) | caso B (aString) => println (aString)"? Parece que siempre y cuando el tipo de cadena a sea idéntico tanto para A como para B, debería permitirse. Su último ejemplo parece que sería mejor no duplicar los casos B y C. –

+30

Te llevaré uno más. Creo que sería bueno tener 'caso A (x) | B (x) => println (x) 'se permite donde el tipo de' x' se establece en el límite superior en el sistema de tipos de lo que producen A (x) y B (x). –

4

Bueno, realmente no tiene sentido, ¿o sí? B y C son mutuamente excluyentes, de modo que sb o sc quedan vinculados, pero no se sabe cuál, por lo que necesitaría más lógica de selección para decidir cuál usar (dado que estaban vinculados a una Opción [String], no una cuerda). Así que no hay nada ganado más de esto:

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

O esto:

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

¿Qué pasa si no te importa si B o C coinciden? Digamos en el siguiente código: 'args match { case Array (" - x ", hostArg) => (hostArg, verdadero); caso Array (hostArg, "-x") => (hostArg, true) } ' Sin embargo, veo que no es el caso común, y que la creación de un método local es una alternativa. Sin embargo, si la alternativa es conveniente, entonces no tiene mucho sentido tener alternativas de casos. En realidad, en algunos dialectos ML tiene una característica similar y todavía puede enlazar variables, siempre que (IIRC) sea igual, ya que cada variable está vinculada con el mismo tipo en ambas alternativas. – Blaisorblade

+0

Tiene razón. Si solo le importan los tipos y no los valores, ni qué tipo se presentó, la coincidencia disyuntiva basada en el tipo es significativa y está disponible. –

8

Hay un par de maneras que puedo ver para lograr lo que está después, si tiene algo en común entre clases de casos El primero es hacer que las clases de caso extiendan un rasgo que declara lo común, el segundo es usar un tipo estructural que elimine la necesidad de extender sus clases de casos.

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

El método de tipo estructural genera una advertencia sobre el borrado que, en este momento, no estoy seguro de cómo eliminarlo.

Cuestiones relacionadas