2010-03-01 7 views
9

Perché ottengo un errore quando provo a utilizzare _ invece di utilizzare un identificatore denominato?In Scala, qual è la differenza tra l'uso di `_` e l'utilizzo di un identificatore denominato?

scala> res0 
res25: List[Int] = List(1, 2, 3, 4, 5) 

scala> res0.map(_=>"item "+_.toString) 
<console>:6: error: missing parameter type for expanded function ((x$2) => "item 
".$plus(x$2.toString)) 
     res0.map(_=>"item "+_.toString) 
         ^

scala> res0.map(i=>"item "+i.toString) 
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5) 

risposta

18

I caratteri di sottolineatura utilizzati al posto di nomi di variabili come quelli speciali; l'ennesimo underscore significa l'ennesimo argomento ad una funzione anonima. Così i seguenti sono equivalenti:

List(1, 2, 3).map(x => x + 1) 

List(1, 2, 3).map(_ + 1) 

Ma, se si esegue questa operazione:

List(1, 2, 3).map(_ => _ + 1) 

Poi si sta mappando la lista con una funzione che ignora il suo singolo argomento e restituisce la funzione definita da _ + 1. (Questo esempio specifico non verrà compilato perché il compilatore non può dedurre che tipo di sottolineatura il secondo ha.) Un esempio equivalente con parametri denominati sarebbe simile:

List(1, 2, 3).map(x => { y => y + 1 }) 

In breve, utilizzando sottolineature nella lista degli argomenti di una funzione significa "Sto ignorando questi argomenti nel corpo di questa funzione". Usarli nel corpo significa "Compilatore, per favore genera un elenco di argomenti per me". I due usi non si combinano molto bene.

+1

@Scoobie Solo per rinforzarlo, il carattere di sottolineatura viene utilizzato per molti scopi _differenti in Scala. Come ha spiegato David, ogni uso nel tuo esempio ha in realtà un significato diverso. Ci sono anche altri significati - sottolineatura, in Scala, è un buon esempio di problemi derivanti dal sovraccarico dell'operatore. Inizialmente ho avuto dei problemi, posso onestamente dire che non ho mai pensato ad un modo per migliorarlo. –

3

Se non si intende associare un identificatore, è sufficiente lasciare tale parte.

res0.map("item "+_.toString) 
4

Per completare le altre risposte, ecco alcuni esempi che mostrano il motivo per cui si ottiene il "tipo di parametro mancante" in alcuni casi quando si utilizza '_' come parametro segnaposto.

L'inferenza del tipo di Scala considera il tipo 'previsto' di un'espressione in base al suo contesto. Se non esiste un contesto, non può inferire il tipo di parametri. Si noti nel messaggio di errore la prima e la seconda istanza di _ vengono sostituite con gli identificatori del compilatore x$1 e x$2.

scala> _ + _ 
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     _ + _ 
    ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     _ + _ 
     ^

Aggiunta di un tipo di attribuzione per l'intera espressione fornisce abbastanza contesto per aiutare l'inferencer:

scala> (_ + _) : ((Int, Int) => Int) 
res3: (Int, Int) => Int = <function2> 

In alternativa, è possibile aggiungere un tipo di attribuzione ad ogni parametro segnaposto:

scala> (_: Int) + (_: Int)   
res4: (Int, Int) => Int = <function2> 

Nella chiamata di funzione sottostante con gli argomenti di tipo forniti, il contesto non è ambiguo e il tipo di funzione viene dedotto.

scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2) 
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R 

scala> bar[Int, Int](1, 1, _ + _) 
res5: Int = 2 

Tuttavia, se chiediamo il compilatore di dedurre i parametri di tipo, se fallisce:

scala> bar(1, 1, _ + _)   
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
       ^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
        ^

possiamo farne a meno, però, dai currying le liste di parametri. Qui, gli argomenti del primo elenco di parametri (1, 1) indicano che il parametro di tipo A deve essere Int. Quindi sa che il tipo dell'argomento f deve essere (Int, Int) => ?) e il tipo di ritorno R viene dedotto come Int, il risultato dell'aggiunta di un intero. Vedrete lo stesso approccio utilizzato in Traversable.flatMap nella libreria standard.

scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2) 
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R 

scala> foo[Int, Int](1, 1) { _ + _ } 
res1: Int = 2 

scala> foo(1, 1) { _ + _ } 
res0: Int = 2 
Problemi correlati