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?
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])
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 varargchild ++ 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.
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.