2010-12-07 7 views
18

Para crear una nueva clase que se puede utilizar en un Scala para la comprensión, parece que todo lo que tiene que hacer es definir una función de mapa:¿por qué tiene que definirse el filtro para la coincidencia de patrones en un bucle for en Scala?

scala> class C[T](items: T*) { 
    | def map[U](f: (T) => U) = this.items.map(f) 
    | } 
defined class C 

scala> for (x <- new C(1 -> 2, 3 -> 4)) yield x 
res0: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

Pero eso sólo funciona para simple para bucles donde no hay coincidencia de patrón en el lado izquierdo de <-. Si intenta modelar el partido allí, se obtiene una queja de que el método filter no está definido:

scala> for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 
<console>:7: error: value filter is not a member of C[(Int, Int)] 
     for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 

¿Por qué es el filtro necesario para aplicar el ajuste de patrones aquí? Yo hubiera pensado Scala acaba de traducir el bucle de arriba en el equivalente map llamada:

scala> new C(1 -> 2, 3 -> 4).map{case (k, v) => k -> v} 
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

pero que parece funcionar bien, por lo que el bucle debe traducirse en algo más. ¿A qué se traduce eso que necesita el método filter?

Respuesta

18

La respuesta corta: de acuerdo a las especificaciones Scala, que no es necesario definir un método de 'filtro' por el ejemplo que dio, pero hay una open bug que los medios actualmente es requerido.

La respuesta larga: el algoritmo desugaring aplicado para las comprensiones se describe en the Scala language specification. Vamos a empezar con la sección 6.19 "Para de comprensión y Ciclos For" (estoy mirando a la versión 2.9 de la especificación):

En un primer paso, cada generador de p < - e, donde p no es irrefutable (§ 8.1) para el tipo de e se reemplaza por p < - e.withFilter {case p => true; case _ => false}

El punto importante para su pregunta es si el patrón en la comprensión es "irrefutable" para la expresión dada o no. (El patrón es el bit anterior al '< -'; la expresión es el bit posterior). Si es "irrefutable", no se agregará el filtro con, de lo contrario será necesario.

Bien, pero ¿qué significa "irrefutable"? Avance a la sección 8.1.14 de la especificación ("Patrones irrefutables"). En términos generales, si el compilador puede demostrar que el patrón no puede fallar al hacer coincidir la expresión, entonces el patrón es irrefutable y no se agregará la llamada con filtro.

Ahora su ejemplo que funciona como se espera es el primer tipo de patrón irrefutable de la sección 8.1.14, un patrón variable. Así que el primer ejemplo es fácil para el compilador para determinar que withFilter no es necesario.

Su segundo ejemplo es potencialmente el tercer tipo de patrón irrefutable, un patrón de constructor. Intentar hacer coincidir (k, v) que es Tuple2[Any,Any] con un Tuple2[Int,Int] (consulte la sección 8.1.6 y 8.1.7 de la especificación)) se realiza correctamente ya que Int es irrefutable para Any. Por lo tanto, el segundo patrón también es irrefutable y no necesita (no debería) un método withFilter.

En el ejemplo de Daniel, Tuple2[Any,Any] no es irrefutable contra Any, por lo que las llamadas con filtro se agregan.

Por cierto, el mensaje de error habla de un método filter pero la especificación habla de withFilter - se cambió con Scala 2.8, consulte this question and answer para los detalles sangrientos.

12

ver la diferencia:

scala> for ((k, v) <- List(1 -> 2, 3 -> 4, 5)) yield k -> v 
res22: List[(Any, Any)] = List((1,2), (3,4)) 

scala> List(1 -> 2, 3 -> 4, 5).map{case (k, v) => k -> v} 
scala.MatchError: 5 
+0

Comprobé el código compilado con y sin '5'. Cuando no hay '5', el filtro no se usa. – IttayD

+11

Wow, nunca me di cuenta de que para los bucles arrojaría silenciosamente elementos que no concuerden. Eso suena como un gotcha sutil, es mejor que tenga cuidado. – Steve

+0

La versión 'map' es casi tan peligrosa, ya que no hay ninguna advertencia de que pueda arrojarla. Desafortunadamente, actualmente no existe una sintaxis para desestructurar los argumentos en un literal de función sin usar una función parcial. –

Cuestiones relacionadas