2011-07-18 13 views
6

sé que se puede hacer coincidente en las listas de una manera comojuego con combinaciones personalizadas/operadores

val list = List(1,2,3) 
list match { 
    case head::tail => head 
    case _ => //whatever 
} 

así que empecé a preguntarse cómo funciona esto. Si he entendido bien, :: es sólo un operador, por lo que lo es para que dejara de hacer algo como

4 match { 
    case x + 2 => x //I would expect x=2 here 
} 

Si hay una manera de crear este tipo de funcionalidad, ¿cómo se hace; si no, ¿por qué?

+0

¿Por qué no usar 'Y-2' (cuando y = 4)? –

+0

Esto es más una curiosidad, así que no tengo ningún ejemplo específico, pero imaginé que esto sería bueno para las clases más complejas. – Dylan

Respuesta

8

La coincidencia de patrones toma la entrada y la descompone con una función unapply. Entonces, en su caso, unapply(4) tendría que devolver los dos números que suman 4. Sin embargo, hay muchos pares que suman 4, por lo que la función no sabría qué hacer.

Lo que necesita es que el 2 sea accesible de alguna manera para la función unapply. Una clase de caso especial que almacena el 2 trabajaría para esto:

case class Sum(addto: Int) { 
    def unapply(i: Int) = Some(i - addto) 
} 

val Sum2 = Sum(2) 
val Sum2(x) = 5 // x = 3 

(que sería bueno para ser capaz de hacer algo como val Sum(2)(y) = 5 de compacidad, pero Scala no permite extractores parametrizados; ver here.)

[EDIT: Esto es un poco tonto, pero en realidad se podría hacer lo siguiente también:

val `2 +` = Sum(2) 
val `2 +`(y) = 5 // y = 3 

]

EDIT: la razón por la cual funciona head::tail es que hay exactamente una forma de dividir el encabezado de la cola de una lista.

No hay nada intrínsecamente especial acerca :: frente +: usted podría utilizar + si has tenido una idea predeterminada de cómo lo quería romper un número. Por ejemplo, si desea + en el sentido de "dividir a la mitad", entonces se podría hacer algo como:

object + { 
    def unapply(i: Int) = Some(i-i/2, i/2) 
} 

y utilizarlo como:

scala> val a + b = 4 
a: Int = 2 
b: Int = 2 

scala> val c + d = 5 
c: Int = 3 
d: Int = 2 

EDIT: Por último, this explica que, cuando coincidencia de patrón, A op B significa lo mismo que op(A,B), lo que hace que la sintaxis se vea bien.

+0

Creo que tu edición aclara las cosas para mí, voy a tener que juguetear con este concepto más tarde esta noche – Dylan

+0

@Dylan: He reescrito la prosa por lo que espero que sea menos confuso ahora. – dhg

4

a juego con case head :: tail utiliza un patrón de operación infija de la forma p1 op p2 cual se traduce a op(p1, p2) antes de hacer el juego real. (Ver API para ::)

El problema con + es la siguiente:

Si bien es fácil de añadir un objeto

object + { 
    def unapply(value: Int): Option[(Int, Int)] = // ... 
} 

la que haría el juego, sólo se puede suministrar un resultado por valor. P.ej.

object + { 
    def unapply(value: Int): Option[(Int, Int)] = value match { 
    case 0 => Some(0, 0) 
    case 4 => Some(3, 1) 
    case _ => None 
} 

Ahora bien, esto funciona:

0 match { case x + 0 => x } // returns 0 

también esta

4 match { case x + 1 => x } // returns 3 

pero esto no será y no puede cambiarlo:

4 match { case x + 2 => x } // does not match 

No hay problema para ::, sin embargo, porque es siempre se define qué es head y qué es tail de una lista.

+0

+1 para el enlace API. No me di cuenta. Entonces, por lo que deduzco de eso, una Lista [_] es en realidad una subclase de :: y por lo tanto una clase de caso y ¿por eso puede coincidir así? Así que si quería ofrecer algún tipo de juego como ' "hola" partido {case cabeza :: cola // h + Ello' ... entonces yo tendría que hacer una conversión implícita de una cadena a mi propia clase caso especializado? – Dylan

1

Hay dos :: s (pronunciado "contras") en Scala. Uno de ellos es el operador en List s y el otro es una clase, que representa una lista no vacía caracterizado por una cabeza y una cola. Entonces head :: tail es un patrón de constructor, que no tiene nada que ver con el operador.

Cuestiones relacionadas