web-dev-qa-db-fra.com

Que fait `: _ *` (étoile du soulignement du côlon) en Scala?

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 clair, sauf cette pièce: child ++ newChild : _*

Qu'est ce que ça fait?

Je comprends qu'il y a Seq[Node] _ concaténé avec un autre Node, et ensuite? Qu'est-ce que : _* faire?

182
amorfis

C'est "splats"1 la séquence.

Regardez la signature du constructeur

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

qui s'appelle comme

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

mais ici il n'y a qu'une séquence, pas child1, child2, etc., ceci permet donc d’utiliser la séquence de résultats comme entrée du constructeur.

Bonne codage.


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

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

Le dernier paramètre de valeur d’une section de paramètre peut être défini par “*”, par ex. (..., x: T *). Le type d'un tel paramètre répété dans la méthode est alors le type de séquence scala.Seq [T]. Les méthodes à 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ù tous les noms de paramètres autres que ps sont frais. La seule exception à cette règle est si le dernier argument est marqué comme étant un 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 pris (p1: T1,..., pn: Tn, ps: scala.Seq [S])

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

child ++ newChild : _* se développe Seq[Node] à Node* (indique au compilateur que nous travaillons plutôt avec une variable, plutôt qu’une séquence). Particulièrement utile pour les méthodes acceptant uniquement les variables.

86
Vasil Remeniuk

Toutes les réponses ci-dessus ont l'air super, mais il suffit d'un échantillon pour expliquer cela. 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 que :_* do est de 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 :_* consiste à supprimer toute ambiguïté lorsqu’on passe l’argument au paramètre vararg.

18
Keith