O que `: _ *` (estrela de dois pontos) faz em Scala?

195

Eu tenho o seguinte pedaço de código desta pergunta :

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

Tudo está bem claro, exceto esta peça: child ++ newChild : _*

O que isso faz?

Eu entendo que é Seq[Node]concatenado com outro Node, e então? O que : _*faz?

amorfis
fonte
70
Muito obrigado por adicionar (estrela de dois pontos) ao título!
Gal

Respostas:

151

Ele "divide" 1 a sequência.

Veja a assinatura do construtor

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

que é chamado como

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

mas aqui há apenas uma sequência, não child1, child2etc., portanto, isso permite que a sequência de resultados seja usada como entrada para o construtor.

Feliz codificação.


1 Isso não tem um nome atraente no SLS, mas aqui estão os detalhes. O importante a ser obtido é que ele altera como o Scala vincula os argumentos ao método com parâmetros repetidos (conforme indicado Node*acima).

A _*anotação de tipo é abordada em "4.6.2 Parâmetros repetidos" do SLS.

O último parâmetro de valor de uma seção de parâmetro pode ser sufixo por "*", por exemplo (..., x: T *). O tipo de um parâmetro repetido dentro do método é o tipo de sequência scala.Seq [T]. Métodos com parâmetros repetidos T * recebem um número variável de argumentos do tipo T. Ou seja, se um método m com o tipo (p1: T1,.., Pn: Tn, ps: S *) U é aplicado aos argumentos (e1,.., Ek) em que k> = n, então m é tomado nesse aplicativo para ter o tipo (p1: T1,..., pn: Tn, ps: S,.., ps0S) U, com k ¡n ocorrências do tipo S em que quaisquer nomes de parâmetros além de ps são novos.A única exceção a essa regra é se o último argumento estiver marcado como um argumento de sequência por meio de uma anotação do tipo _ *. Se m acima for aplicado aos argumentos (e1,..., En, e0: _ *), o tipo de m nesse aplicativo será considerado (p1: T1,..., Pn: Tn, ps: scala .Seq [S])

Roman Kagan
fonte
5
Nós gostamos de chamá-lo de "operador Smooch", mesmo que ele não é realmente um operador :)
Henrik Gustafsson
1
Em Python isso é chamado de desempacotamento
joshlk
Existe um limite para quanto tempo a sequência pode ser, como existe com varargs Java?
Qqqwwq 3/17
95
  • child ++ newChild - seqüência
  • : - atribuição de tipo, uma dica que ajuda o compilador a entender, que tipo tem essa expressão
  • _* - espaço reservado que aceita qualquer valor + operador vararg

child ++ newChild : _*expande Seq[Node]para Node*(informa ao compilador que estamos trabalhando com um varargs, em vez de uma sequência). Particularmente útil para os métodos que podem aceitar apenas varargs.

Vasil Remeniuk
fonte
1
Você poderia escrever mais sobre "atribuição de tipo"? O que é e como funciona?
Amorfis 19/05/11
24

Toda a resposta acima parece ótima, mas só precisamos de uma amostra para explicar isso. Aqui 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]*

Então agora sabemos o que :_*fazer é dizer ao compilador: descompacte esse argumento e vincule esses elementos ao parâmetro vararg na chamada de função, em vez de considerar x como um único argumento.

Portanto, resumindo, :_*é remover a ambiguidade quando passar o argumento para o parâmetro vararg.

Keith
fonte
5

Para algumas pessoas preguiçosas como eu, apenas converte um Seq em varArgs!

mani_nz
fonte