Eu tenho o seguinte trecho 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 parte: child ++ newChild : _*
O que isso faz?
Eu entendo que há Seq[Node]
concatenado com outro Node
, e então? O que faz : _*
fazer?
Ele “splats” 1 a seqüência.
Olhe para 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
, child2
, etc., portanto, isso permite que a sequência de resultados seja usada como input para o construtor.
Codificação feliz.
1 Isso não tem um nome bonitinho no SLS, mas aqui estão os detalhes. O importante é que ele mude como o Scala liga os argumentos ao método com parâmetros repetidos (como denotado com o Node*
acima).
A anotação tipo _*
é abordada em “4.6.2 parameters 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 é então o tipo de seqüência scala.Seq [T]. Métodos com parâmetros repetidos T * obtém um número variável de argumentos do tipo T. Isto é, se um método m com tipo (p1: T1,.., Pn: Tn, ps: S *) U é aplicado aos argumentos (e1, .e, ek) onde k> = n, então m é tomadas naquela aplicação para ter tipo (p1: T1, …, pn: Tn, ps: S, …, ps0S) U, com k ¡n ocorrências do tipo S onde qualquer nome de parâmetro além de ps é novo. A única exceção a essa regra é se o último argumento for marcado como um argumento de seqüência por meio de uma anotação de tipo _ *. Se m acima for aplicado aos argumentos (e1, …, en, e0: _ *), então o tipo de m nessa aplicação é considerado como sendo (p1: T1, …, pn: Tn, ps: scala Seq [S]
child ++ newChild
– sequência :
– digite ascription, uma dica que ajuda o compilador a entender, que tipo essa expressão tem _*
– espaço reservado aceitando qualquer valor + operador vararg child ++ newChild : _*
expande Seq[Node]
para Node*
(diz ao compilador que estamos trabalhando bastante com varargs, do que com uma sequência). Particularmente útil para os methods que podem aceitar apenas varargs.
Toda a resposta acima parece ótima, mas só precisa 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 :_*
do é dizer ao compilador: por favor, descompacte este argumento e ligue esses elementos ao parâmetro vararg na chamada de function, em vez de tomar o x como um único argumento.
Então, em poucas palavras, o :_*
é para remover ambiguidade quando passar argumento para o parâmetro vararg.