Que fait `: _ *` (étoile de soulignement du colon) dans Scala?


200

J'ai le morceau de code suivant de cette question :

def addChild(n: Node, newChild: Node) = n match {
  case Elem(prefix, label, attribs, scope, child @ _*) => Elem(prefix, label, attribs, scope, child ++ newChild : _*)
  case _ => error("Can only add children to elements!")
}

Tout y est assez clair, sauf cette pièce: child ++ newChild : _*

Qu'est ce que ça fait?

Je comprends qu'il est Seq[Node]concaténé avec un autre Node, et puis? Que fait : _*-on?


71
Merci beaucoup d'avoir ajouté (étoile de soulignement deux points) au titre!
Gal

Réponses:


155

Il "splats" 1 la séquence.

Regardez la signature du constructeur

new Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding,
         child: Node*)

qui est appelé comme

new Elem(prefix, label, attributes, scope,
         child1, child2, ... childN)

mais ici il n'y a qu'une séquence, non child1, child2etc. donc cela permet à la séquence de résultat à utiliser comme entrée au constructeur.

Bon codage.


1 Cela n'a pas de nom mignon dans le SLS, mais voici les détails. La chose importante à obtenir est que cela change la façon dont Scala lie les arguments à la méthode avec des paramètres répétés (comme indiqué Node*ci-dessus).

L' _*annotation de type est traitée dans "4.6.2 Paramètres répétés" du SLS.

Le dernier paramètre de valeur d'une section de paramètres peut être suffixé par «*», par exemple (..., x: T *). Le type d'un tel paramètre répété à l'intérieur de la méthode est alors le type de séquence scala.Seq [T]. Les méthodes avec des paramètres répétés T * prennent un nombre variable d'arguments de type T. Autrement dit, si une méthode m de type (p1: T1,..., Pn: Tn, ps: S *) U est appliquée aux arguments (e1,..., Ek) où k> = n, alors m est pris dans cette application pour avoir le type (p1: T1,..., pn: Tn, ps: S,..., ps0S) U, avec k ¡n occurrences de type S où tout nom de paramètre au-delà de ps est frais.La seule exception à cette règle est si le dernier argument est marqué comme argument de séquence via une annotation de type _ *. Si m ci-dessus est appliqué aux arguments (e1,..., En, e0: _ *), alors le type de m dans cette application est considéré comme (p1: T1,..., Pn: Tn, ps: scala .Seq [S])


5
Nous aimons l'appeler "l'opérateur Smooch", même si ce n'est pas réellement un opérateur :)
Henrik Gustafsson

1
En Python, cela s'appelle le déballage
joshlk

Y a-t-il une limite à la durée de la séquence, comme c'est le cas avec les varargs Java?
qwwqwwq

95
  • child ++ newChild - séquence
  • : - attribution de type, un indice qui aide le compilateur à comprendre, quel type cette expression a-t-elle
  • _* - espace réservé acceptant n'importe quelle valeur + opérateur vararg

child ++ newChild : _*se développe Seq[Node]en Node*(indique au compilateur que nous travaillons plutôt avec un varargs qu'avec une séquence). Particulièrement utile pour les méthodes qui ne peuvent accepter que des varargs.


1
Pourriez-vous en dire plus sur "l'attribution de type"? Qu'est-ce que c'est et comment est-ce que ça marche?
amorfis


25

Toutes les réponses ci-dessus sont superbes, mais il suffit d'un échantillon pour l'expliquer. C'est ici :

val x : Seq[Seq[Int]] = Seq(Seq(1),Seq(2))

def f(arg: Seq[Any]*) : Int = {
 arg.length
}
f(x) //1 as x is taken as single arg
f(x:_*)  // 2 as x is "unpacked" as a Seq[Any]*

Alors maintenant, nous savons ce :_*qu'il faut dire au compilateur: veuillez décompresser cet argument et lier ces éléments au paramètre vararg dans l'appel de fonction plutôt que de prendre le x comme un seul argument.

Donc, en un mot, le :_*est de supprimer l'ambiguïté lorsque vous passez l'argument au paramètre vararg.


6

Pour certains paresseux comme moi, cela convertit simplement un Seq en varArgs!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.