¿Qué hace `: _ *` (estrella de subrayado de dos puntos) en Scala?

195

Tengo el siguiente código de esta pregunta :

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!")
}

Todo en él está bastante claro, excepto esta pieza: child ++ newChild : _*

¿Qué hace?

Entiendo que hay Seq[Node]concatenado con otro Node, y luego? ¿Qué : _*hacer?

amorfis
fuente
70
¡Muchas gracias por agregar (estrella de subrayado de dos puntos) al título!
Gal

Respuestas:

151

Se "salpica" 1 la secuencia.

Mira la firma del constructor

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

que se llama como

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

pero aquí solo hay una secuencia, no child1, child2etc., por lo que esto permite que la secuencia de resultados se use como entrada para el constructor.

Feliz codificación


1 Esto no tiene un nombre cursi en el SLS, pero aquí están los detalles. Lo importante es que cambia la forma en que Scala vincula los argumentos al método con parámetros repetidos (como se indica Node*anteriormente).

La _*anotación de tipo se trata en "4.6.2 Parámetros repetidos" del SLS.

El último parámetro de valor de una sección de parámetros puede tener el sufijo "*", por ejemplo (..., x: T *). El tipo de dicho parámetro repetido dentro del método es entonces el tipo de secuencia scala.Seq [T]. Los métodos con parámetros repetidos T * toman un número variable de argumentos de tipo T. Es decir, si un método m con tipo (p1: T1,..., Pn: Tn, ps: S *) U se aplica a los argumentos (e1,..., Ek) donde k> = n, entonces m es tomado en esa aplicación para tener el tipo (p1: T1,..., pn: Tn, ps: S,..., ps0S) U, con k en n ocurrencias de tipo S donde cualquier nombre de parámetro más allá de ps es nuevo.La única excepción a esta regla es si el último argumento está marcado como un argumento de secuencia mediante una anotación de tipo _ *. Si se aplica m arriba a los argumentos (e1,..., En, e0: _ *), entonces se considera que el tipo de m en esa aplicación es (p1: T1,..., Pn: Tn, ps: scala .Seq [S])

Roman Kagan
fuente
55
Nos gusta llamarlo el "operador de Smooch", a pesar de que en realidad no es un operador :)
Henrik Gustafsson
1
En Python esto se llama desempaquetado
joshlk
¿Existe un límite en cuanto a la duración de la secuencia, como en el caso de los varargs de Java?
qwwqwwq
95
  • child ++ newChild - secuencia
  • : - type ascription, una pista que ayuda al compilador a comprender, qué tipo tiene esa expresión
  • _* - marcador de posición que acepta cualquier valor + operador vararg

child ++ newChild : _*se expande Seq[Node]a Node*(le dice al compilador que estamos trabajando más bien con un varargs que con una secuencia). Particularmente útil para los métodos que pueden aceptar solo varargs.

Vasil Remeniuk
fuente
1
¿Podría escribir más sobre "tipo de adscripción"? ¿Qué es y cómo funciona?
amorfis
24

Toda la respuesta anterior se ve genial, pero solo necesita una muestra para explicar esto. Aquí está :

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]*

Entonces, ahora sabemos qué :_*hacer para decirle al compilador: desempaquete este argumento y vincule esos elementos al parámetro vararg en la llamada a la función en lugar de tomar la x como un argumento único.

En pocas palabras, :_*es eliminar la ambigüedad cuando se pasa el argumento al parámetro vararg.

Keith
fuente
5

Para algunas personas perezosas como yo, ¡simplemente convierte un Seq en varArgs!

mani_nz
fuente