Apa yang dilakukan `: _ *` (tanda bintang garis bawah) di Scala?

195

Saya memiliki bagian kode berikut dari pertanyaan ini :

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

Semua yang ada di dalamnya cukup jelas, kecuali bagian ini: child ++ newChild : _*

Apa fungsinya?

Saya mengerti ada Seq[Node]disatukan dengan yang lain Node, dan kemudian? Apa yang : _*harus dilakukan

amorfis
sumber
70
Terima kasih banyak telah menambahkan (tanda bintang garis bawah) ke judul!
Gal

Jawaban:

151

Itu "percikan" 1 urutannya.

Lihatlah tanda tangan konstruktor

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

yang disebut sebagai

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

tetapi di sini hanya ada urutan, bukan child1,, child2dll. jadi ini memungkinkan urutan hasil yang akan digunakan sebagai input ke konstruktor.

Selamat coding.


1 Ini tidak memiliki nama imut dalam SLS, tetapi di sini adalah rinciannya. Yang penting untuk didapatkan adalah bahwa ia mengubah cara Scala mengikat argumen ke metode dengan parameter berulang (seperti dilambangkan dengan di Node*atas).

The _*penjelasan jenis tercakup dalam "4.6.2 Parameter berulang" dari SLS.

Parameter nilai terakhir dari bagian parameter dapat sufiks dengan "*", misalnya (..., x: T *). Jenis parameter berulang seperti itu di dalam metode kemudian jenis urutan scala.Seq [T]. Metode dengan parameter berulang T * mengambil sejumlah variabel argumen tipe T. Yaitu, jika metode m dengan tipe (p1: T1, ..., pn: Tn, ps: S *) U diterapkan pada argumen (e1,.,., Ek) di mana k> = n, maka m adalah diambil dalam aplikasi itu untuk memiliki tipe (p1: T1, ..., pn: Tn, ps: S, ..., ps0S) U, dengan kejadian k dari tipe S di mana setiap nama parameter di luar ps adalah segar.Satu-satunya pengecualian untuk aturan ini adalah jika argumen terakhir ditandai sebagai argumen urutan melalui anotasi tipe _ *. Jika m di atas diterapkan pada argumen (e1, ..., en, e0: _ *), maka jenis m dalam aplikasi itu dianggap (p1: T1, ..., pn: Tn, ps: scala .Seq [S])

Roman Kagan
sumber
5
Kami suka menyebutnya "operator Smooch", meskipun sebenarnya bukan operator :)
Henrik Gustafsson
1
Dalam Python ini disebut unpacking
joshlk
Apakah ada batasan untuk berapa lama urutannya, seperti yang ada dengan Java varargs?
qwwqwwq
95
  • child ++ newChild - urutan
  • : - type ascription, sebuah petunjuk yang membantu kompiler memahami, tipe apa yang dimiliki oleh ekspresi itu
  • _* - placeholder menerima nilai + operator vararg

child ++ newChild : _*expand Seq[Node]to Node*(memberi tahu kompiler bahwa kita agak bekerja dengan varargs, daripada berurutan). Sangat berguna untuk metode yang hanya dapat menerima varargs.

Vasil Remeniuk
sumber
1
Bisakah Anda menulis lebih banyak tentang "ketik anggapan"? Apa itu dan bagaimana cara kerjanya?
amorfis
24

Semua jawaban di atas tampak hebat, tetapi hanya perlu sampel untuk menjelaskan ini. Ini dia :

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

Jadi sekarang kita tahu apa yang :_*harus dilakukan untuk memberitahu compiler: tolong buka paket argumen ini dan ikat elemen-elemen tersebut ke parameter vararg dalam pemanggilan fungsi daripada mengambil x sebagai argumen tunggal.

Jadi singkatnya, :_*adalah untuk menghapus ambiguitas ketika meneruskan argumen ke parameter vararg.

Keith
sumber
5

Untuk beberapa orang malas seperti saya, itu hanya mengubah Seq menjadi varArgs!

mani_nz
sumber