Co robi `: _ *` (gwiazda dwukropka) w Scali?

195

Mam następujący fragment kodu z tego pytania :

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

Wszystko w nim jest dość jasne, z wyjątkiem tego kawałka: child ++ newChild : _*

Co to robi?

Rozumiem, że istnieje Seq[Node]powiązanie z innym Node, a następnie? Co ma : _*zrobić?

Amorfis
źródło
70
Bardzo dziękuję za dodanie (tytułu podkreślenia dwukropka) do tytułu!
Gal

Odpowiedzi:

151

To „splats” 1 sekwencję.

Spójrz na podpis konstruktora

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

który nazywa się jako

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

ale tutaj jest tylko sekwencja, nie child1, child2itp więc pozwala to sekwencja wynik zostać wykorzystane jako wejście do konstruktora.

Szczęśliwego kodowania.


1 To nie ma uroczej nazwy w SLS, ale oto szczegóły. Ważną rzeczą do uzyskania jest to, że zmienia sposób, w jaki Scala wiąże argumenty z metodą z powtarzanymi parametrami (jak Node*wskazano powyżej).

_*Typ adnotacji jest pokryta „4.6.2 Powtarzające parametry” SLS.

Ostatni parametr wartości sekcji parametru może być uzupełniony przez „*”, np. (..., x: T *). Typem takiego powtarzanego parametru w metodzie jest zatem typ sekwencji scala.Seq [T]. Metody z powtarzanymi parametrami T * przyjmują zmienną liczbę argumentów typu T. To znaczy, jeśli metoda m z typem (p1: T1,.., Pn: Tn, ps: S *) U jest stosowana do argumentów (e1,.., Ek), gdzie k> = n, to m jest wzięte w tej aplikacji, aby mieć typ (p1: T1,., pn: Tn, ps: S,.., ps0S) U, z k ¡n wystąpieniami typu S, gdzie nazwy parametrów poza ps są świeże.Jedynym wyjątkiem od tej reguły jest to, że ostatni argument jest oznaczony jako argument sekwencji za pomocą adnotacji typu _ *. Jeśli m powyżej stosuje się do argumentów (e1,.., En, e0: _ *), wówczas przyjmuje się, że typ m w tej aplikacji to (p1: T1,.., Pn: Tn, ps: scala .Seq [S])

Roman Kagan
źródło
5
Lubimy to nazywać „operatorem Smooch”, mimo że tak naprawdę nie jest operatorem :)
Henrik Gustafsson
1
W Pythonie nazywa się to rozpakowywaniem
joshlk
Czy istnieje limit długości sekwencji, na przykład w przypadku varargs Java?
qwwqwwq
95
  • child ++ newChild - sekwencja
  • : - type ascription, wskazówka, która pomaga kompilatorowi zrozumieć, jaki typ ma to wyrażenie
  • _* - symbol zastępczy akceptujący dowolną wartość + operator vararg

child ++ newChild : _*rozwija się Seq[Node]do Node*(mówi kompilatorowi, że raczej pracujemy z varargs niż z sekwencją). Szczególnie przydatny w metodach, które mogą akceptować tylko varargs.

Wasil Remeniuk
źródło
1
Czy możesz napisać więcej o „przypisaniu typu”? Co to jest i jak działa?
amorfis
24

Wszystkie powyższe odpowiedzi wyglądają świetnie, ale wystarczy próbka, aby to wyjaśnić. Oto on:

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

Teraz wiemy, co :_*należy powiedzieć kompilatorowi: rozpakuj ten argument i powiąż te elementy z parametrem vararg w wywołaniu funkcji, zamiast traktować x jako pojedynczy argument.

Krótko mówiąc, :_*ma to na celu usunięcie niejednoznaczności podczas przekazywania argumentu do parametru vararg.

Keith
źródło
5

Dla niektórych leniwych ludzi, takich jak ja, konwertuje Seq na varArgs!

mani_nz
źródło