Was nützt ein Unterstrich in Scala?


540

Ich habe mir die Liste der auf scala-lang.org durchgeführten Umfragen angesehen und eine merkwürdige Frage festgestellt: " Können Sie alle Verwendungszwecke von" _ "nennen? ". Können Sie? Wenn ja, bitte hier. Erklärende Beispiele werden geschätzt.


15
Ich habe diese anständigen Folien vor nicht allzu langer Zeit gelesen: Scala Dreaded Underscore
Dan Burton

5
Siehe auch The Dreaded _ Präsentation auf Slideshare
Mifeet

Antworten:


576

Die, an die ich denken kann, sind

Existenzielle Typen

def foo(l: List[Option[_]]) = ...

Höher sortierte Typparameter

case class A[K[_],T](a: K[T])

Ignorierte Variablen

val _ = 5

Ignorierte Parameter

List(1, 2, 3) foreach { _ => println("Hi") }

Ignorierte Namen von Selbsttypen

trait MySeq { _: Seq[_] => }

Platzhaltermuster

Some(5) match { case Some(_) => println("Yes") }

Platzhaltermuster in Interpolationen

"abc" match { case s"a$_c" => }

Sequenz-Platzhalter in Mustern

C(1, 2, 3) match { case C(vs @ _*) => vs.foreach(f(_)) }

Platzhalterimporte

import java.util._

Importe verstecken

import java.util.{ArrayList => _, _}

Briefe an Operatoren verbinden

def bang_!(x: Int) = 5

Zuweisungsoperatoren

def foo_=(x: Int) { ... }

Platzhaltersyntax

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

Methodenwerte

List(1, 2, 3) foreach println _

Konvertieren von Call-by-Name-Parametern in Funktionen

def toFunction(callByName: => Int): () => Int = callByName _

Standardinitialisierer

var x: String = _   // unloved syntax may be eliminated

Es kann andere geben, die ich vergessen habe!


Beispiel zeigt warum foo(_)und foo _sind unterschiedlich:

Dieses Beispiel stammt von 0__ :

trait PlaceholderExample {
  def process[A](f: A => Unit)

  val set: Set[_ => Unit]

  set.foreach(process _) // Error 
  set.foreach(process(_)) // No Error
}

Stellt im ersten Fall process _eine Methode dar; Scala verwendet die polymorphe Methode und versucht, sie durch Ausfüllen des Typparameters monomorph zu machen, stellt jedoch fest, dass kein Typ ausgefüllt Awerden kann, der den Typ ergibt (_ => Unit) => ?(Existential _ist kein Typ).

Im zweiten Fall process(_)ist ein Lambda; Wenn Sie ein Lambda ohne expliziten Argumenttyp schreiben foreach, _ => Unit leitet Scala den Typ aus dem erwarteten Argument ab und ist ein Typ (während dies einfach _nicht der Fall ist), sodass er ersetzt und abgeleitet werden kann.

Dies ist wahrscheinlich das schwierigste Problem in Scala, dem ich je begegnet bin.

Beachten Sie, dass dieses Beispiel in 2.13 kompiliert wird. Ignorieren Sie es, als wäre es dem Unterstrich zugewiesen worden.


4
Ich denke, es gibt zwei oder drei, die alle unter die Verwendung von Unterstrichen beim Mustervergleich passen, aber +1, um Buchstaben mit Interpunktion zu verbinden! :-)
Daniel C. Sobral

22
val x: Any = _
Giovanni Botta

2
@Owen Ich glaube nicht, dass println _ eine teilweise angewendete Funktion ist. Es ist ein weiteres Beispiel für die Platzhaltersyntax, oder? Bedeutung map (_ + 2) erweitert sich zu etwas ähnlichem wie map (x => x + 2), genauso wie pritnln (_) sich zu etwas ähnlichem wie map erweitert (x => println (x))
Andrew Cassidy

7
@ AndrewCassidy Eigentlich println _und println(_)sind anders. Sie können dies zum Beispiel daran sehen, dass sie existenzielle und polymorphe Typen etwas unterschiedlich behandeln. Ich werde gleich ein Beispiel finden.
Owen

3
@ AndrewCassidy OK Ich habe ein Beispiel hinzugefügt.
Owen

179

Aus (meinem Eintrag) in den FAQ , deren Vollständigkeit ich sicherlich nicht garantieren kann (ich habe erst vor zwei Tagen zwei Einträge hinzugefügt):

import scala._    // Wild card -- all of Scala is imported
import scala.{ Predef => _, _ } // Exception, everything except Predef
def f[M[_]]       // Higher kinded type parameter
def f(m: M[_])    // Existential type
_ + _             // Anonymous function placeholder parameter
m _               // Eta expansion of method into method value
m(_)              // Partial function application
_ => 5            // Discarded parameter
case _ =>         // Wild card pattern -- matches anything
val (a, _) = (1, 2) // same thing
for (_ <- 1 to 10)  // same thing
f(xs: _*)         // Sequence xs is passed as multiple parameters to f(ys: T*)
case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence
var i: Int = _    // Initialization to the default value
def abc_<>!       // An underscore must separate alphanumerics from symbols on identifiers
t._2              // Part of a method name, such as tuple getters
1_000_000         // Numeric literal separator (Scala 2.13+)

Dies ist auch Teil dieser Frage .


2
var i: Int = _val (a, _) = (1, 2)for (_ <- 1 to 10) doIt()
Vielleicht

1
Und eine def f: T; def f_=(t: T)Kombination zum Erstellen eines veränderlichen f-Mitglieds.
Huynhjl

Der Mustervergleich wird bereits behandelt, und _bei Methodennamen wird geschummelt. Aber gut, ok. Ich hoffe nur, dass jemand anderes die FAQ aktualisiert ... :-)
Daniel C. Sobral

1
Vielleicht vermisst du diesen. vertx.newHttpServer.websocketHandler (_. writeXml (html))
angelokh

@angelokh Das ist der anonyme Funktionsplatzhalterparameter, fünfter in der Liste.
Daniel C. Sobral

84

Eine ausgezeichnete Erklärung für die Verwendung des Unterstrichs ist Scala _ [Unterstrich] Magie .

Beispiele:

 def matchTest(x: Int): String = x match {
     case 1 => "one"
     case 2 => "two"
     case _ => "anything other than one and two"
 }

 expr match {
     case List(1,_,_) => " a list with three element and the first element is 1"
     case List(_*)  => " a list with zero or more elements "
     case Map[_,_] => " matches a map with any key type and any value type "
     case _ =>
 }

 List(1,2,3,4,5).foreach(print(_))
 // Doing the same without underscore: 
 List(1,2,3,4,5).foreach( a => print(a))

In Scala verhält es sich beim Importieren von Paketen _ähnlich wie *in Java.

// Imports all the classes in the package matching
import scala.util.matching._

// Imports all the members of the object Fun (static import in Java).
import com.test.Fun._

// Imports all the members of the object Fun but renames Foo to Bar
import com.test.Fun.{ Foo => Bar , _ }

// Imports all the members except Foo. To exclude a member rename it to _
import com.test.Fun.{ Foo => _ , _ }

In Scala werden ein Getter und ein Setter implizit für alle nicht privaten Variablen in einem Objekt definiert. Der Getter-Name entspricht dem Variablennamen und _=wird für den Setter-Namen hinzugefügt.

class Test {
    private var a = 0
    def age = a
    def age_=(n:Int) = {
            require(n>0)
            a = n
    }
}

Verwendungszweck:

val t = new Test
t.age = 5
println(t.age)

Wenn Sie versuchen, einer neuen Variablen eine Funktion zuzuweisen, wird die Funktion aufgerufen und das Ergebnis der Variablen zugewiesen. Diese Verwirrung tritt aufgrund der optionalen geschweiften Klammern für den Methodenaufruf auf. Wir sollten _ nach dem Funktionsnamen verwenden, um ihn einer anderen Variablen zuzuweisen.

class Test {
    def fun = {
        // Some code
    }
    val funLike = fun _
}

2
Das ist eine gute Erklärung, aber es gibt nicht einmal alle. Es fehlen ignorierte Parameter / Variablen, das Verbinden von Buchstaben und Interpunktion, existenzielle Typen, höher sortierte Typen
Owen

in Ihrem ist List(1,2,3,4,5).foreach(print(_))es viel besser lesbar, nur zu tun List(1,2,3,4,5).foreach(print), Sie brauchen nicht einmal wirklich den Unterstrich, aber ich denke, das ist nur eine Frage des Stils
Electric Coffee

1
Wie wäre es mit "_" als Platzhalter in Sammlungen mit der Funktion .map, .flatten, .toList ...... Manchmal macht es mich falsch. :(
m0z4rt

34

Es gibt eine Verwendung, die ich sehen kann. Jeder hier scheint vergessen zu haben, sie aufzulisten ...

Anstatt dies zu tun:

List("foo", "bar", "baz").map(n => n.toUpperCase())

Sie können dies einfach tun:

List("foo", "bar", "baz").map(_.toUpperCase())

Also _ fungiert hier als Namespace aller verfügbaren Funktionen?
Crt

2
@Crt nein, es dient als Abkürzung fürn => n
Electric Coffee

2
Ist dies nicht die in den beiden oberen Antworten erwähnte Platzhaltersyntax?
Joelb

13

Hier sind einige weitere Beispiele, wo _verwendet wird:

val nums = List(1,2,3,4,5,6,7,8,9,10)

nums filter (_ % 2 == 0)

nums reduce (_ + _)

nums.exists(_ > 5)

nums.takeWhile(_ < 8)

In allen obigen Beispielen repräsentiert ein Unterstrich ein Element in der Liste (zum Reduzieren repräsentiert der erste Unterstrich den Akkumulator).


11

Neben den von JAiro erwähnten Verwendungen gefällt mir diese:

def getConnectionProps = {
    ( Config.getHost, Config.getPort, Config.getSommElse, Config.getSommElsePartTwo )
}

Wenn jemand alle Verbindungseigenschaften benötigt, kann er Folgendes tun:

val ( host, port, sommEsle, someElsePartTwo ) = getConnectionProps

Wenn Sie nur einen Host und einen Port benötigen, können Sie Folgendes tun:

val ( host, port, _, _ ) = getConnectionProps

0

Es gibt ein spezielles Beispiel für die Verwendung von "_":

  type StringMatcher = String => (String => Boolean)

  def starts: StringMatcher = (prefix:String) => _ startsWith prefix

kann gleich sein:

  def starts: StringMatcher = (prefix:String) => (s)=>s startsWith prefix

Wenn Sie in einigen Szenarien "_" anwenden, wird automatisch in "(x $ n) => x $ n" konvertiert.


Ich glaube, das Beispiel eines jeden ist ein Element der Iteration. Ich denke, dies ist eher ein
Syntaxzucker auf
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.