2010-07-10 19 views
7

appena visto una possibilità interessante per inizializzare i blocchi di codice a Scala per le funzioni di ordine superiore, come foreach o mappa:Scala foreach e mappa inizializzatori

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


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

E 'questa una caratteristica documentata o dovrei evitare tali costrutti? Potrei immaginare, il blocco di "inizializzazione" entra nel costruttore e la chiusura stessa diventa un metodo apply()?

Grazie Pat per la domanda iniziale (http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline)

risposta

12

Mentre le funzioni utilizzate non sono rare, devo ammettere che è una combinazione abbastanza strana di funzionalità. Il trucco di base è che qualsiasi blocco in Scala è un'espressione, con il tipo uguale all'ultima espressione nel blocco. Se quell'ultima espressione è una funzione, ciò significa che il blocco ha un tipo funzionale, e quindi può essere usato come argomento per "mappare" o "foreach". Quello che succede in questi casi è che quando viene chiamata "map" o "foreach", il blocco viene valutato. Il blocco valuta una funzione (i => i * 5 nel primo caso) e tale funzione viene quindi mappata nell'intervallo.

Un possibile utilizzo di questo costrutto è che il blocco definisca le variabili mutabili e che la funzione risultante muti le variabili ogni volta che viene chiamata. Le variabili verranno inizializzate una volta, chiuse dalla funzione e i loro valori aggiornati ogni volta che viene chiamata la funzione.

Per esempio, ecco un modo un po 'sorprendente di calcolare i primi 6 numeri fattoriali

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

(BTW, mi spiace per l'utilizzo fattoriale come esempio. Era uno che o Fibonacci. Funzionali regole Progamming Guild. Devi avere problemi con quello, portalo con i ragazzi in fondo alla sala.)

Un motivo meno imperativo per avere una funzione di ritorno di blocco è definire le funzioni di supporto in precedenza nel blocco. Ad esempio, se il secondo esempio erano invece

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

Il risultato sarebbe che tre linee sono state leggere e una volta fatto eco, mentre il vostro esempio ha avuto la linea letto una volta e fece eco tre volte.

+0

Risposta molto più precisa della mia. +1 – VonC

+0

Nell'esempio fattoriale, dovresti usare 'total * = i' invece di introdurre una seconda variabile chiamata' counter' –

+0

Sì, l'ho capito più tardi. Modificherà –

1

In primo luogo, il commento del blog originale "Scala Question Regarding readLine" post menzione

Il “line” è un valore e non può essere eseguito, è assegnato una sola volta dal risultato dell'esecuzione del metodo "Console.readLine".
Viene utilizzato meno di tre volte nella chiusura.
Ma se si definisce come un metodo, verrà eseguito tre volte:

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

Il blog Scala for Java Refugees Part 6: Getting Over Java ha una interessante sezione sulla funzione ordine superiore, tra cui:

Scala fornisce ancora maggiore flessibilità nella sintassi per queste funzioni di ordine superiore.
Nel invocazione iterata, stiamo creando un intero metodo anonimo solo per fare un'altra chiamata al metodo println(String).
Considerando println(String) è esso stesso un metodo che prende un String e restituisce Unit, si potrebbe pensare di poterlo comprimere un po '. Come si è visto, siamo in grado di:

iterate(a, println) 

Omettendo le parentesi e semplicemente specificando il nome del metodo, stiamo dicendo al compilatore Scala che vogliamo usare println come un valore funzionale, passandolo al metodo iterate.
Così invece di creare un nuovo metodo solo per gestire un unico insieme di chiamate, si passa in un vecchio metodo che già fa quello che vogliamo.
Questo è un pattern comunemente visto in C e C++. In effetti, la sintassi per il passaggio di una funzione come valore funzionale è esattamente la stessa. Sembra che alcune cose non cambino mai ...