2010-10-24 15 views
19

Este código es de Querying a Dataset with Scala's Pattern Matching:explicar este patrón de código de juego

object & { def unapply[A](a: A) = Some((a, a)) } 

"Julie" match { 
    case Brothers(_) & Sisters(_) => "Julie has both brother(s) and sister(s)" 
    case Siblings(_) => "Julie's siblings are all the same sex" 
    case _ => "Julie has no siblings" 
} 

// => "Julie has both brother(s) and sister(s)" 

¿Cómo & funcionan realmente? No veo una prueba booleana en ninguna parte para la conjunción. ¿Cómo funciona esta magia Scala?

+1

Vea también: http://stackoverflow.com/questions/2261358/pattern-matching-with-conjunctions-patterna-and-patternb – retronym

Respuesta

29

Así es como unapply obras en general:

Al hacer

obj match {case Pattern(foo, bar) => ... } 

Pattern.unapply(obj) se llama. Esto puede devolver None en cuyo caso la coincidencia de patrón es una falla, o Some(x,y) en cuyo caso foo y bar están limitados a x y y.

Si en lugar de Pattern(foo, bar) que hizo Pattern(OtherPattern, YetAnotherPatter) continuación x sería comparada con el patrón OtherPattern y y podría ser comparada con YetAnotherPattern. Si todas las coincidencias de patrones son exitosas, el cuerpo de la coincidencia se ejecuta, de lo contrario se intentará el siguiente patrón.

cuando el nombre de un patrón no es alfanumérico, sino un símbolo (como &), se utiliza infija, es decir, se escribe en lugar de foo & bar&(foo, bar).


Así que aquí & es un patrón que siempre devuelve Some(a,a) no importa lo que es a. Entonces & siempre coincide y vincula el objeto coincidente a sus dos operandos. En código que significa que

obj match {case x & y => ...} 

siempre coincidirá y ambos x y y tendrá el mismo valor que obj.

En el ejemplo anterior, esto se usa para aplicar dos patrones diferentes al mismo objeto.

I.e. cuando lo haces

obj match { case SomePattern & SomeOtherPattern => ...}` 

primero se aplica el patrón &. Como dije, siempre coincide y enlaza obj a su LHS y su RHS. Entonces, se aplica SomePattern al LHS & (que es lo mismo que obj) y SomeOtherPattern se aplica al RHS & (que también es lo mismo que obj).

De hecho, acaba de aplicar dos patrones al mismo objeto.

+0

Veo que así es y como es la intención, pero no cómo la definición de y entrega eso . ¿Puedes explicar más? –

+1

@Paul: pensé, lo hice. El patrón '&' toma un objeto y lo devuelve dos veces. Eso es todo lo que hace. Trataré de explicar un poco más, pero eso es básicamente todo lo que hay que hacer. – sepp2k

+0

Sí, es probable que yo sea denso (y un neófito de Scala).Si tiene consejos para cosas que debería haber leído primero, ¡por favor publíquelas! –

1

Para obtener información adicional, le recomiendo leer Infix Operation Patterns sección (8.1.10) del Scala Language Specification.

un infijo patrón de operación p op q es una taquigrafía para el patrón constructor o extractor op(p,q).La precedencia y asociatividad de los operadores en patrones es la misma que en expresiones.

Que es prácticamente todo lo que se necesita, pero luego puede leer sobre patrones y patrones de constructor y extractor en general. Que ayuda a separar el aspecto de azúcar sintáctica (la parte "mágica" de la misma) de la bastante simple idea de coincidencia de patrones:

Un patrón se construye a partir de las constantes, constructores, variables y tipo pruebas. Patrón de búsqueda de prueba si un valor dado (o secuencia de valores) tiene la forma definida por un patrón, y, si lo hace, se une las variables en el patrón a los correspondientes componentes del valor (o secuencia de valores).

7

Hagamos esto desde el código. En primer lugar, una pequeña reescritura:

object & { def unapply[A](a: A) = Some(a, a) } 

"Julie" match { 
    // case Brothers(_) & Sisters(_) => "Julie has both brother(s) and sister(s)" 
    case &(Brothers(_), Sisters(_)) => "Julie has both brother(s) and sister(s)" 
    case Siblings(_) => "Julie's siblings are all the same sex" 
    case _ => "Julie has no siblings" 
} 

La nueva reescritura significa exactamente lo mismo. La línea de comentario usa notación infija para extractores, y la segunda usa notación normal. Ambos se traducen en lo mismo.

Por lo tanto, Scala alimentará "Julie" al extractor, repetidamente, hasta que todas las variables no vinculadas se hayan asignado al objeto Some. El primer extractor es &, así que conseguir esto:

&.unapply("Julie") == Some(("Julie", "Julie")) 

Llegamos Some atrás, por lo que podemos proceder con el partido. Ahora tenemos una tupla de dos elementos, y tenemos dos extractores dentro &, así que vamos a alimentar cada elemento de la tupla a cada extractora:

Brothers.unapply("Julie") == ? 
Sisters.unapply("Julie") == ? 

Si ambos regresan Some cosa, entonces el partido se exitoso Sólo por diversión, vamos a volver a escribir el código sin la coincidencia de modelos:

val pattern = "Julie" 
val extractor1 = &.unapply(pattern) 
if (extractor1.nonEmpty && extractor1.get.isInstanceOf[Tuple2]) { 
    val extractor11 = Brothers.unapply(extractor1.get._1) 
    val extractor12 = Sisters.unapply(extractor1.get._2) 
    if (extractor11.nonEmpty && extractor12.nonEmpty) { 
    "Julie has both brother(s) and sister(s)" 
    } else { 
    "Test Siblings and default case, but I'll skip it here to avoid repetition" 
    } 
} else { 
    val extractor2 = Siblings.unapply(pattern) 
    if (extractor2.nonEmpty) { 
    "Julie's siblings are all the same sex" 
    } else { 
    "Julie has no siblings" 
} 

código de feo aspecto, incluso sin la optimización para conseguir solamente extractor12 si extractor11 no está vacío, y sin la repetición de código que debería haber ido donde hay un comentario . Así que voy a escribir en otro estilo:

val pattern = "Julie" 
& unapply pattern filter (_.isInstanceOf[Tuple2]) flatMap { pattern1 => 
    Brothers unapply pattern1._1 flatMap { _ => 
    Sisters unapply pattern1._2 flatMap { _ => 
     "Julie has both brother(s) and sister(s)" 
    } 
    } 
} getOrElse { 
    Siblings unapply pattern map { _ => 
    "Julie's siblings are all the same sex" 
    } getOrElse { 
    "Julie has no siblings" 
    } 
} 

El patrón de flatMap/map al principio sugiere otra manera de escribir esto:

val pattern = "Julie" 
(
    for { 
    pattern1 <- & unapply pattern 
    if pattern1.isInstanceOf[Tuple2] 
    _ <- Brothers unapply pattern1._1 
    _ <- Sisters unapply pattern1._2 
    } yield "Julie has both brother(s) and sister(s) 
) getOrElse (
for { 
    _ <- Siblings unapply pattern 
} yield "Julie's siblings are all the same sex" 
) getOrElse (
    "julie has no siblings" 
) 

usted debería ser capaz de ejecutar todo esto codificar y ver los resultados por ti mismo.

Cuestiones relacionadas