2014-12-10 12 views
5

Questo codiceCostruire un'espressione lambda utilizzando una sottolineatura

(1 to 30).foreach { x => 
    println(x) 
    println 
} 

fa ciò che mi aspettavo: la stampa di ogni 1-30, intervallati da spazi vuoti. Sono abbastanza chiaro su cosa sta succedendo qui, penso: sto passando una funzione anonima che prima stampa la sua argomentazione, e poi stampa una riga vuota.

Quello che non capisco è il motivo per cui questo non fa lo stesso:

(1 to 30).foreach { 
    println _ 
    println 
} 

Sembra equivalente a me. Il carattere di sottolineatura dovrebbe rappresentare il primo e unico argomento della funzione; e la funzione stampa il suo argomento e quindi stampa una riga vuota. Ma quando eseguo questa seconda versione, non ottengo le righe vuote.

Che cosa causa questa differenza?

risposta

5

La prima variante è semplice:

  1. Nella prima riga, diffusa println su x.
  2. Nella seconda riga, applicare il no-argomento println (stampa la nuova riga aggiuntiva).

Con la seconda variante si raccontano in modo efficace Scala per fare questo:

  1. Nella prima riga, definire un oggetto funzione da println(). Successivamente, non fare nulla con questo oggetto appena creato.
  2. Nella seconda riga, applicare println all'argomento (l'elemento della sequenza ).

La confusione deriva dal presupposto che println(x) e println _ sono equivalenti. Sono diversi. La sintassi funcId _ definisce una nuova funzione basata su funcId, non è uguale all'utilizzo della notazione "argomento di sottolineatura" quando si chiama una funzione.

3

C'è un numero di cose che succedono qui.

Prima di tutto, la sintassi del segnaposto dei parametri può essere utilizzata solo tra parentesi esterne della definizione lambda. Non può essere utilizzato tra parentesi delle chiamate al metodo eseguite all'interno della definizione lambda.

Ecco un esempio per dimostrare questo punto.

val a = (1 to 10).map(_ + 1) 

Questo funzionerà.

val b = (1 to 10).map(math.sin(_ + 1)) 

Questo non funzionerà.

Pertanto, il codice non utilizza affatto la sintassi del segnaposto dei parametri. Usa invece funzioni parzialmente applicate.

Ad esempio

(1 to 10).foreach(println _) 

è funzionalmente uguale

val a = println (_ : Int) 
(1 to 10).foreach(a) 

anche quando un nome metodo viene utilizzato all'interno espressione lambda la sottolineatura può essere omesso. Scala genererà comunque il metodo parzialmente applicato.

Pertanto

(1 to 10).foreach(println) 

è uguale a

(1 to 10).foreach(println _) 

E quindi il codice è uguale alla

val a = println (_ : Int) 
    (1 to 10).foreach{ 
    a 
    a 
    } 

E perché {AA} restituisce una, è pari a

val a = println (_ : Int) 
(1 to 10).foreach(a) 
0

Per aggiungere ad altre risposte, esiste effettivamente un modo per utilizzare println(_) e non dichiarare x parametro:

(1 to 30).foreach { 
    (println(_: Int)) 
    .andThen(_ => println) 
} 

Qui foreach parametro è funzione che richiama dapprima println(_) per l'elemento intervallo e quindi passa println(Int) risultato (che è (): Unit) a un'altra funzione, _ => println, che ignora il suo argomento e stampa una nuova riga.

Problemi correlati