2010-03-01 12 views
9

Warum erhalte ich einen Fehler, wenn ich versuche, _ anstelle eines benannten Bezeichners zu verwenden?Worin besteht in Scala der Unterschied zwischen der Verwendung des _ und der Verwendung eines benannten Bezeichners?

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) 

Antwort

18

Unterstriche anstelle von Variablennamen wie diese sind spezielle; Der N-te Unterstrich bedeutet das N-te Argument einer anonymen Funktion. So sind die folgend äquivalent:

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

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

Aber, wenn Sie dies tun:

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

Dann zuordnen Sie die Liste mit einer Funktion, die sein einziges Argument ignoriert und gibt die Funktion von _ + 1 definiert. (Dieses spezifische Beispiel wird nicht kompilieren, da der Compiler nicht ableiten kann, welche Art der zweite Strich hat.) Ein äquivalentes Beispiel mit dem Namen Parameter würde wie folgt aussehen:

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

Kurz gesagt, Unterstrichen in einer Funktion Argumentliste mit bedeutet "Ich ignoriere diese Argumente im Körper dieser Funktion." Wenn man sie im Körper benutzt, heißt das "Compiler, bitte erzeuge eine Argumentliste für mich." Die zwei Nutzungen sind nicht sehr gut.

+1

@Scoobie Nur um dies zu verstärken, wird der Unterstrich für viele verschiedene Zwecke in Scala verwendet. Wie David erklärte, hat jede Verwendung in Ihrem Beispiel tatsächlich eine andere Bedeutung. Es gibt noch andere Bedeutungen - Unterstreichung, in Scala, ist ein ziemlich gutes Beispiel für Probleme, die sich aus der Überlastung von Operatoren ergeben. Während ich anfangs Probleme damit hatte, kann ich ehrlich sagen, dass ich noch nie darüber nachgedacht habe, wie ich es verbessern könnte. –

3

Wenn Sie keinen Bezeichner binden wollen, lassen Sie diesen Teil einfach weg.

4

Um die anderen Antworten zu ergänzen, hier einige Beispiele, die zeigen, warum Sie in einigen Fällen den "fehlenden Parametertyp" erhalten, wenn Sie '_' als Platzhalterparameter verwenden.

Scala-Typ-Inferenz berücksichtigt den 'erwarteten' Typ eines Ausdrucks basierend auf seinem Kontext. Wenn kein Kontext vorhanden ist, kann der Typ der Parameter nicht abgeleitet werden. Beachten Sie, dass in der Fehlermeldung die ersten und zweiten Instanzen von _ durch die vom Compiler generierten Kennungen x$1 und x$2 ersetzt werden.

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)) 
     _ + _ 
     ^

einen Typ Zuschreibung auf den gesamten Ausdruck liefert die Rückschließer helfen genug Kontext hinzu:

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

Alternativ Sie einen Typ Zuschreibung auf jeden Parameter Platzhalter hinzufügen:

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

Im folgenden Funktionsaufruf mit den angegebenen Typargumenten ist der Kontext eindeutig und der Funktionstyp wird abgeleitet.

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 

Wenn wir jedoch bitten, den Compiler die Typparameter zu schließen, wenn nicht:

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, _ + _) 
        ^

Wir es helfen kann, aber durch die Parameterlisten currying. Hier sagen die Argumente für die erste Parameterliste (1, 1) die Folgerung, dass der Typparameter AInt sein sollte. Er weiß dann, dass der Typ des Arguments f(Int, Int) => ?) sein muss, und der Rückgabetyp R wird als Int abgeleitet, das Ergebnis der Ganzzahladdition. Sie sehen den gleichen Ansatz, der in der Standardbibliothek in Traversable.flatMap verwendet wird.

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 
Verwandte Themen