`: _ *` (Dấu gạch dưới dấu hai chấm) làm gì trong Scala?


195

Tôi có đoạn mã sau từ câu hỏi này :

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

Mọi thứ trong đó đều khá rõ ràng, ngoại trừ tác phẩm này: child ++ newChild : _*

Nó làm gì?

Tôi hiểu có liên Seq[Node]kết với người khác Node, và sau đó? Không gì : _*làm gì?


70
Cảm ơn bạn rất nhiều vì đã thêm (dấu gạch dưới dấu hai chấm) vào tiêu đề!
Gal

Câu trả lời:


151

Nó "splats" 1 chuỗi.

Nhìn vào chữ ký của nhà xây dựng

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

được gọi là

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

nhưng ở đây chỉ có một chuỗi, không child1, child2v.v. vì vậy điều này cho phép chuỗi kết quả được sử dụng làm đầu vào cho hàm tạo.

Chúc mừng mã hóa.


1 Cái này không có tên dễ thương trong SLS, nhưng đây là chi tiết. Điều quan trọng cần có là nó thay đổi cách Scala liên kết các đối số với phương thức với các tham số lặp lại (như được biểu thị Node*ở trên).

Các _*loại chú thích được bao phủ trong "4.6.2 Các thông số lặp đi lặp lại" của SLS.

Tham số giá trị cuối cùng của một phần tham số có thể được xác định bởi bởi * *, ví dụ (..., x: T *). Kiểu của một tham số lặp lại như vậy bên trong phương thức sau đó là kiểu chuỗi scala.Seq [T]. Các phương thức có tham số lặp lại T * lấy số lượng đối số loại T. Nghĩa là, nếu một phương thức m có kiểu (p1: T1, .., Pn: Tn, ps: S *) U được áp dụng cho các đối số (e1, .., Ek) trong đó k> = n, thì m là lấy trong ứng dụng đó để có loại (p1: T1, .., pn: Tn, ps: S, .., ps0S) U, với các lần xuất hiện của loại S trong đó bất kỳ tên tham số nào ngoài ps đều mới.Ngoại lệ duy nhất cho quy tắc này là nếu đối số cuối cùng được đánh dấu là đối số chuỗi thông qua chú thích kiểu _ *. Nếu m ở trên được áp dụng cho các đối số (e1, .., En, e0: _ *), thì loại m trong ứng dụng đó được lấy là (p1: T1, .., Pn: Tn, ps: scala .Seq [S])


5
Chúng tôi muốn gọi nó là "Nhà điều hành Smooch", mặc dù nó không thực sự là một nhà điều hành :)
Henrik Gustafsson

1
Trong Python, điều này được gọi là giải nén
joshlk

Có giới hạn nào về trình tự có thể kéo dài bao lâu, chẳng hạn như có các varargs Java không?
qwwqwwq

95
  • child ++ newChild - sự nối tiếp
  • : - gõ mô tả, một gợi ý giúp trình biên dịch hiểu, biểu thức đó có kiểu gì
  • _* - giữ chỗ chấp nhận bất kỳ giá trị + toán tử vararg

child ++ newChild : _*mở rộng Seq[Node]tới Node*(nói với trình biên dịch rằng chúng ta đang làm việc với một varargs, hơn là một chuỗi). Đặc biệt hữu ích cho các phương pháp chỉ có thể chấp nhận varargs.


1
Bạn có thể viết thêm về "loại mô tả"? Nó là gì và nó hoạt động như thế nào?
amorfis


24

Tất cả các câu trả lời trên có vẻ tuyệt vời, nhưng chỉ cần một mẫu để giải thích điều này. Đây là:

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

Vì vậy, bây giờ chúng ta biết :_*phải làm gì để nói với trình biên dịch: vui lòng giải nén đối số này và liên kết các phần tử đó với tham số vararg trong lệnh gọi hàm thay vì lấy x làm đối số duy nhất.

Vì vậy, một cách ngắn gọn, đó :_*là loại bỏ sự mơ hồ khi truyền đối số cho tham số vararg.


5

Đối với một số người lười biếng như tôi, nó chỉ chuyển đổi một Seq thành varArss!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.