2010-09-20 21 views
23

He encontrado que Scala siempre tiene una "explicación natural" para cualquier cosa. Siempre algo como "ohh, pero eso es solo una función que se llama en este y ese objeto con este y ese parámetro". En cierto sentido, nada es realmente compilador, como lo conocemos desde otros idiomas.Scala "<-" para comprensión

Mi pregunta es sobre la <- operador tal como se utiliza en el siguiente código:

for(i <- 0 to 10) println(i) 

En este ejemplo puedo ver que está siendo reescrito para algo como:

0.to(10).foreach((i:Int)=>println(i)) 

pero esto no explica cómo se llevó el i al anon ymous función dentro de la función foreach. En el punto donde escribe i no es un objeto, y aún no es una variable declarada. Entonces, ¿qué es y cómo se transmite al interior de Foreach?

Mi conjetura es que, finalmente, descubrí algo que es de hecho compilador magia

Gracias por su tiempo.

Para aclarar, mi pregunta es: ¿Cómo funciona el < - operador de trabajo en la primera línea de código, ya que no es un objeto sobre el que se puede llamar como una función.

Respuesta

18

<- es un lenguaje -símbolo de palabra clave definido, como es => pero en claro contraste con -> (que es un símbolo definido). Debido a que es parte de la gramática básica de Scala, se puede usar para crear enlaces (para el i en su ejemplo) que es algo que las construcciones definidas por el usuario no pueden hacer.

+0

Esta parece ser la respuesta. Le sugiero que documente esto, si es posible, en este momento es un poco inesperado. – Felix

+4

@Felix: está documentado en la especificación. Y casi todos los libros de Scala disponibles en el mercado lo cubren. – missingfaktor

+0

oh sí, aún no tengo un libro. esperando el libro definitivo 2.8 – Felix

6

En este caso, realmente es un poco de compilación mágica. La traducción de for-comprehension a filter/map/flatmap form es un proceso especial de desugaring, muy parecido a la conversión de las formas especiales de actualización y aplicación de métodos.

57

para aumentar la respuesta de Dave, aquí es un esquema de traducción de la 'para-comprensiones' de especificación del lenguaje Scala:

Una comprensión for (enums) yield e evalúa la expresión e para cada unión generada por el enumeraciones enumeradores. Una secuencia de enumerador siempre comienza con un generador; esto puede ser seguido por generadores adicionales, definiciones de valores o guardias.

Un generador p <- e produce enlaces a partir de una expresión e que coincide de alguna manera con el patrón p. Una definición de valor val p = e vincula el nombre de valor p (o varios nombres en un patrón p) al resultado de evaluar la expresión e. Un guardia if e contiene una expresión booleana que restringe los enlaces enumerados.

El significado preciso de los generadores y los guardias se define por la traducción a las invocaciones de cuatro métodos: map, filter, flatMap y foreach. Estos métodos se pueden implementar de diferentes maneras para diferentes tipos de operadores.

El esquema de traducción es el siguiente. En un primer paso, cada generador p <- e, donde p no es irrefutable (§8.1) para el tipo de e se sustituye por

p <- e.filter { case p => true; case _ => false } 

Entonces, las siguientes reglas se aplican repetidamente hasta que todos los comprensiones han sido eliminado.

  • A para la comprensión-for (p <- e) yield e0 se traduce a e.map { case p => e0 }.

  • A comprensión for (p <- e) e0 se traduce en e.foreach { case p => e0 }.

  • A comprensión for (p <- e; p0 <- e0 . . .) yield e00, donde. . . es una secuencia (posiblemente vacía) de generadores o guardias, se traduce a:
    e.flatMap { case p => for (p0 <- e0 . . .) yield e00 }.

  • A comprensión for (p <- e; p0 <- e0 . . .) e00 donde. . . es una secuencia (posiblemente vacía) de generadores o protectores, se traduce a:
    e.foreach { case p => for (p0 <- e0 . . .) e00 }.

  • Un generador p <- e seguido por un guardia if g se traduce a un único generador:
    p <- e.filter((x1, . . . , xn) => g)
    donde x1,. . . , xn son las variables gratuitas de p.

  • Un generador p <- e seguido de una definición de valor val p0 = e0 se traduce a la siguiente generador de pares de valores, donde x y x0 son nombres frescos:

    val (p, p0) <- 
        for([email protected] <- e) yield { val [email protected] = e0; (x, x0) } 
    
+1

Bien, no entiendo todo después de la primera lectura, pero es interesante :-) ¿De dónde sacaste esto? – Felix

+0

@Felix: Como dije en la parte superior de la respuesta, es de la especificación del idioma (se puede descargar desde www.scala-lang.org) – missingfaktor

Cuestiones relacionadas