Que signifie param: _ * dans Scala?

87

Étant nouveau dans Scala (2.9.1), j'ai un List[Event]et je voudrais le copier dans un Queue[Event], mais la syntaxe suivante donne un à la Queue[List[Event]]place:

val eventQueue = Queue(events)

Pour une raison quelconque, ce qui suit fonctionne:

val eventQueue = Queue(events : _*)

Mais j'aimerais comprendre ce que ça fait, et pourquoi ça marche? J'ai déjà regardé la signature de la Queue.applyfonction:

def apply[A](elems: A*)

Et je comprends pourquoi la première tentative ne fonctionne pas, mais quelle est la signification de la seconde? Qu'est-ce que :, et _*dans ce cas, et pourquoi la applyfonction ne prend- elle pas simplement un Iterable[A]?

Chris
la source

Réponses:

93

a: Aest une attribution de type; voir Quel est le but des attributions de type dans Scala?

: _* est une instance spéciale d'attribution de type qui indique au compilateur de traiter un seul argument d'un type séquence comme une séquence d'arguments variable, c'est-à-dire varargs.

Il est tout à fait valide de créer une Queueutilisation Queue.applyqui a un seul élément qui est une séquence ou itérable, c'est donc exactement ce qui se passe lorsque vous en donnez un Iterable[A].

Ben James
la source
83

Il s'agit d'une notation spéciale qui indique au compilateur de transmettre chaque élément comme son propre argument, plutôt que le tout comme un seul argument. Regardez ici .

C'est une annotation de type qui indique un argument de séquence et qui est mentionnée comme une "exception" à la règle générale de la section 4.6.2 de la spécification du langage, "Paramètres répétés".

C'est utile lorsqu'une fonction prend un nombre variable d'arguments, par exemple une fonction telle que def sum(args: Int*), qui peut être appelée comme sum(1), sum(1,2)etc. Si vous avez une liste telle que xs = List(1,2,3), vous ne pouvez pas se passer xs, car c'est un Listplutôt qu'un Int, mais vous pouvez passer ses éléments en utilisant sum(xs: _*).

Luigi Plinge
la source
def sum(xs: _*)jette 'erreur: type générique non lié'
7kemZmani
Votre réponse est claire, mais cela crée en fait plus de confusion pour moi, généralement dans scala xs: intsignifie que le type de xs est int, en passant par là, une syntaxe dans scala où xs: _*signifie que xs est casté vers ses membres individuels.
Rpant
Suivez le lien ci-dessus et ressemble à ce que c'est, l'attribution de type est une terminologie scala pour le casting de type java. Veuillez me corriger si vous avez tort.
Rpant
1
@ 7kemZmani: Vous devez définir la fonction avec un type var-args spécifique: def sum(args: Int*)et vous l' appelez avec le caractère générique de type var-args « générique »: val a = sum(xs: _*). Pensez à _*«Je passe un Int *, ou une chaîne *, ou tout ce qui est défini dans la signature de la méthode»
Alfonso Nishikawa
10

Pour les gens de Python:

L' _*opérateur de Scala est plus ou moins l'équivalent de l' opérateur * de Python .


Exemple

Conversion de l'exemple scala à partir du lien fourni par Luigi Plinge :

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

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

en Python ressemblerait à:

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

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

et les deux donnent la sortie suivante:

Quoi de
neuf
doc?


La différence: déballage des paramètres de position

Alors que l' *opérateur de Python peut également gérer le déballage des paramètres / paramètres de position pour les fonctions à arité fixe:

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

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

8

Faire de même avec Scala:

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

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

échouera:

pas assez d'arguments pour la méthode multipliée: (x: Int, y: Int) Int.
Paramètre de valeur non spécifié y.

Mais il est possible de réaliser la même chose avec scala:

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

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

Selon Lorrin Nelson, voici comment cela fonctionne:

La première partie, f _, est la syntaxe d'une fonction partiellement appliquée dans laquelle aucun des arguments n'a été spécifié. Cela fonctionne comme un mécanisme pour mettre la main sur l'objet de fonction. tupled renvoie une nouvelle fonction d'arity-1 qui prend un seul tuple arity-n.

Lire plus:

Murmel
la source