2010-07-10 17 views
7

acaba de ver una posibilidad interesante para inicializar los bloques de código en Scala para funciones de orden superior tales como foreach o mapa:Scala foreach y mapa inicializadores

(1 to 3) map { 
    val t = 5 
    i => i * 5 
} 


(1 to 3) foreach { 
    val line = Console.readLine 
    i => println(line) 
} 

Es esto una característica documentada o debería evitar este tipo de construcciones? Me podría imaginar, el bloque de "inicialización" entra en el constructor y el cierre en sí mismo se convierte en un método de apply()?

Gracias Pat por la pregunta original (http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline)

Respuesta

12

Si bien las características utilizadas no son poco comunes, voy a admitir que es una combinación bastante extraña de características. El truco básico es que cualquier bloque en Scala es una expresión, con tipo igual a la última expresión en el bloque. Si esa última expresión es una función, esto significa que el bloque tiene un tipo funcional y, por lo tanto, puede usarse como argumento para "mapear" o "llegar". Lo que sucede en estos casos es que cuando se llama "mapa" o "foreach", se evalúa el bloque. El bloque evalúa una función (i => i * 5 en el primer caso), y esa función luego se mapea sobre el rango.

Un posible uso de esta construcción es que el bloque defina variables mutables, y la función resultante mute las variables cada vez que se llame. Las variables serán inicializadas una vez, cerradas por la función, y sus valores actualizados cada vez que se llame a la función.

Por ejemplo, aquí está una manera un tanto sorprendente de cálculo de los primeros 6 números factoriales

(1 to 6) map { 
     var total = 1 
     i => {total *= i;total} 
    } 

(Por cierto, lo siento por el uso factorial como un ejemplo. Era eso o funcionales reglas Progamming del gremio de Fibonacci.. Tienes problema con eso, tómalo con los chicos en la sala.)

Una razón menos imperativa para tener una función de retorno de bloque es definir las funciones auxiliares antes en el bloque. Por ejemplo, si su segundo ejemplo eran lugar

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

El resultado sería que las tres líneas se lee y se hizo eco una vez cada uno, mientras que su ejemplo tenía la línea lee una vez y se hizo eco de tres veces.

+0

Respuesta mucho más precisa que la mía. +1 – VonC

+0

En el ejemplo factorial, debe usar 'total * = i' en lugar de introducir una segunda variable llamada' contador' –

+0

Sí, me di cuenta más tarde. Editará –

1

primer lugar, el comentario de la original del blog "Scala Question Regarding readLine" post mención

El “line” es un valor y no se puede ejecutar, es asignado una sola vez desde el resultado de la ejecución del método "Console.readLine".
Se usa menos de tres veces en su cierre.
Pero si se define como un método, se ejecutó a tres veces:

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

El blog Scala for Java Refugees Part 6: Getting Over Java tiene una interesante sección en función de orden superior, incluyendo:

Scala ofrece todavía más flexibilidad en la sintaxis para estas funciones de orden superior.
En la invocación de iteración, estamos creando un método anónimo completo solo para realizar otra llamada al método println(String).
Considerando que println(String) es en sí mismo un método que toma String y devuelve Unit, se podría pensar que podríamos comprimir esto un poco. Como resultado, podemos:

iterate(a, println) 

Al omitir los paréntesis y simplemente especificando el nombre del método, le estamos diciendo al compilador Scala que queremos utilizar println como un valor funcional, pasándole al método iterate.
Por lo tanto, en lugar de crear un nuevo método solo para manejar un solo conjunto de llamadas, pasamos un método antiguo que ya hace lo que queremos.
Este es un patrón que se ve comúnmente en C y C++. De hecho, la sintaxis para pasar una función como un valor funcional es exactamente la misma. Parece que algunas cosas nunca cambian ...