Was bedeutet param: _ * in Scala?

87

Da ich neu in Scala (2.9.1) bin, habe ich ein List[Event]und möchte es in ein kopieren Queue[Event], aber die folgende Syntax ergibt Queue[List[Event]]stattdessen ein:

val eventQueue = Queue(events)

Aus irgendeinem Grund funktioniert Folgendes:

val eventQueue = Queue(events : _*)

Aber ich würde gerne verstehen, was es tut und warum es funktioniert? Ich habe mir bereits die Signatur der Queue.applyFunktion angesehen:

def apply[A](elems: A*)

Und ich verstehe, warum der erste Versuch nicht funktioniert, aber was bedeutet der zweite? Was ist :und _*in diesem Fall und warum nimmt die applyFunktion nicht einfach eine Iterable[A]?

Chris
quelle

Antworten:

89

a: Aist Typzuschreibung; siehe Was ist der Zweck von Typzuschreibungen in Scala?

: _* ist eine spezielle Instanz der Typzuweisung, die den Compiler anweist, ein einzelnes Argument eines Sequenztyps als variable Argumentsequenz, dh varargs, zu behandeln.

Es ist völlig gültig, eine QueueVerwendung zu erstellen Queue.apply, die ein einzelnes Element enthält, das eine Sequenz oder iterierbar ist. Genau dies passiert also, wenn Sie ein einzelnes Element angeben Iterable[A].

Ben James
quelle
81

Dies ist eine spezielle Notation, die den Compiler anweist, jedes Element als eigenes Argument und nicht als einzelnes Argument zu übergeben. Siehe hier .

Es handelt sich um eine Typanmerkung, die ein Sequenzargument angibt und als "Ausnahme" von der allgemeinen Regel in Abschnitt 4.6.2 der Sprachspezifikation "Wiederholte Parameter" erwähnt wird.

Es ist nützlich, wenn eine Funktion eine variable Anzahl von Argumenten akzeptiert, z. B. eine Funktion wie def sum(args: Int*), die aufgerufen werden sum(1)kann sum(1,2)usw. Wenn Sie eine Liste wie haben xs = List(1,2,3), können Sie sich nicht xsselbst übergeben, da es sich Listeher um eine als um eine Inthandelt. Sie können die Elemente jedoch mit übergeben sum(xs: _*).

Luigi Plinge
quelle
def sum(xs: _*)wirft 'Fehler: ungebundener Platzhaltertyp'
7kemZmani
Ihre Antwort ist klar, aber dies schafft tatsächlich mehr Verwirrung für mich, normalerweise xs: intbedeutet in scala, dass der Typ von xs int ist. Dies ist die obige Syntax in scala, bei der xs: _*xs in seine einzelnen Mitglieder umgewandelt wird.
Rpant
Folgen Sie dem obigen Link und sehen Sie so aus, wie es ist. Die Typzuweisung ist eine Scala-Terminologie für das Casting von Java-Typ. Bitte korrigieren Sie mich, wenn Sie falsch liegen.
Rpant
1
@ 7kemZmani: Sie müssen die Funktion mit einem bestimmten var-args-Typ definieren: def sum(args: Int*)und Sie rufen sie mit dem Platzhalter "generischen" var-args-Typ auf : val a = sum(xs: _*). Denken Sie an _*"Ich übergebe ein Int * oder einen String * oder irgendetwas *, das in der Methodensignatur definiert ist"
Alfonso Nishikawa
6

Für Python-Leute:

Der _*Operator von Scala entspricht mehr oder weniger dem * -Operator von Python .


Beispiel

Die Umwandlung des scala Beispiel aus dem Link bereitgestellt durch Luigi Plinge :

def echo(args: String*) = 
    for (arg <- args) println(arg)

val arr = Array("What's", "up", "doc?")
echo(arr: _*)

zu Python würde aussehen wie:

def echo(*args):
    for arg in args:
        print "%s" % arg

arr = ["What's", "up", "doc?"]
echo(*arr)

und beide geben die folgende Ausgabe:

Was ist los
,
Doc?


Der Unterschied: Positionsparameter auspacken

Der Python- *Operator kann sich auch mit dem Auspacken von Positionsparametern / Parametern für Funktionen mit fester Arität befassen:

def multiply (x, y):
    return x * y

operands = (2, 4)
multiply(*operands)

8

Dasselbe mit Scala machen:

def multiply(x:Int, y:Int) = {
    x * y;
}

val operands = (2, 4)
multiply (operands : _*)

wird versagen:

Nicht genügend Argumente für die Methodenmultiplikation: (x: Int, y: Int) Int.
Nicht spezifizierter Wertparameter y.

Aber mit scala ist es möglich, dasselbe zu erreichen:

def multiply(x:Int, y:Int) = {
    x*y;
}

val operands = (2, 4)
multiply _ tupled operands

Laut Lorrin Nelson funktioniert das so:

Der erste Teil, f _, ist die Syntax für eine teilweise angewendete Funktion, in der keines der Argumente angegeben wurde. Dies funktioniert als Mechanismus, um das Funktionsobjekt zu erfassen. tupled gibt eine neue Funktion von arity-1 zurück, die ein einzelnes arity-n-Tupel akzeptiert.

Weitere Lesung:

Murmel
quelle