在Scala中,`:_*`(冒号下划线星号)是什么意思?

242

我有以下一段代码,来自这篇问题:

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

除了以下这条语句: child ++ newChild : _*,其他都很明确。

它是什么意思?

我理解其中有一个Seq[Node]与另一个Node串联在一起,然后呢?: _*是什么意思?


87
非常感谢您将(冒号下划线星号)添加到标题中! - Gal
4个回答

175

它“splats”1序列。

查看构造函数签名。

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

这被称为

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

但这里只有一个序列,没有child1child2等。因此,这允许将结果序列用作构造函数的输入。
1 在SLS中,这没有一个可爱的名字,但以下是详细信息。重要的是要理解它如何改变Scala将参数绑定到具有重复参数的方法(如上面用Node*表示的)的方式。
在SLS的“4.6.2 重复参数”中涵盖了_*类型注释

参数段的最后一个值参数可能带有“*”,例如(..., x:T )。方法内部的重复参数的类型是scala.Seq[T]。带有重复参数T *的方法接受数量可变的类型为T的参数。也就是说,如果将类型为(p1:T1,...,pn:Tn,ps:S)U的方法m应用于参数(e1,...,ek),其中k≥n,则该应用程序中的m被认为具有类型(p1:T1,...,pn:Tn,ps:S,...,ps0S)U,其中任何超出ps的参数名称都是新鲜的,有k-n个S类型的发生。 唯一的例外是,如果最后一个参数通过_类型注释标记为序列参数。如果m以上应用于参数(e1,...,en,e0:_),则在该应用程序中m的类型被认为是(p1:T1,...,pn:Tn,ps:scala.Seq[S])**


8
我们喜欢称它为“Smooch 运算符”,尽管实际上它并不是一个运算符 :) - Henrik Gustafsson
2
在Python中,这被称为解包。 - joshlk
序列的长度是否有限制,比如Java的可变参数? - qwwqwwq
@qwwqwwq 没有,没有限制。除非,也许一个方法可以接受的参数总数有限制。在这种情况下,它将具有相同的限制。 - Mike Williamson

108
  • child ++ newChild - 序列连接操作
  • : - 类型注解,帮助编译器理解表达式的类型。
  • _* - 占位符,接受任何值 + 可变参数操作符。

child ++ newChild : _*Seq[Node] 展开为 Node* (告诉编译器我们更倾向于使用可变参数而不是序列)。 对于只能接受可变参数的方法特别有用。


2
你能写更多关于“类型注释”的内容吗?它是什么,它如何工作? - amorfis
11
什么是Scala中类型注释的目的? 在Scala中,类型注释是指向编译器指定变量或表达式的类型的方式。这可以用来帮助编译器理解代码并捕获潜在的类型错误。类型注释还可以用于强制执行代码的特定类型,以确保代码按预期工作。 - Vasil Remeniuk
2
很好的答案。因此,通过编写a: _*,您告诉编译器将a视为_*的实例,在这种情况下只是Node* - Ming

39

以上所有答案看起来都很棒,但只需要一个示例来解释。

这是示例:

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

所以现在我们知道:_*的作用是告诉编译器:请展开此参数并将这些元素绑定到函数调用中的可变参数,而不是将x作为单个参数传递。

简而言之,:_*的作用是在将参数传递给可变参数时消除歧义。


1
这就是我一直在寻找的答案。其他的也很好,但是这个简单的例子让我感到非常开心。向@Keith致敬。 - George Fandango
这也是另一种很好的例子:这将解包为两个项:Seq(1)Seq(2)。我如何解包到12?我可以使用f(x:_*:_*)吗? - Mike Williamson

13

对于像我这样的懒人,它只是将集合值转换为可变参数!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接