Scala中的`:_ *`(冒号下划线星)是做什么的?


195

我从这个问题有以下代码:

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,然后呢?怎么: _*办?


70
非常感谢您为标题添加(冒号下划线)!
2014年

Answers:


151

它“ 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应用于其中k> = n的参数(e1,...,ek),则m为在该应用程序中具有类型(p1:T1,...,pn:Tn,ps:S,...,ps0S)U,其中k个出现类型S,其中ps以外的任何参数名称都是新鲜的。该规则的唯一例外是,如果最后一个参数通过_ *类型注释标记为序列参数。如果上面的m应用于自变量(e1,...,en,e0:_ *),则该应用程序中的m的类型为(p1:T1,...,pn:Tn,ps:scala .Seq [S])


5
我们喜欢称它为“ Smooch运算符”,即使它实际上不是运算符:)
Henrik Gustafsson

1
在Python中,这称为解
压缩

序列可以有多长,例如Java varargs是否有限制?
qwwqwwq'5

95
  • child ++ newChild - 序列
  • : -类型说明,一种提示,可帮助编译器理解该表达式具有什么类型
  • _* -占位符接受任何值+ vararg运算符

child ++ newChild : _*扩展Seq[Node]Node*(告诉编译器我们正在使用varargs,而不是序列)。对于仅接受可变参数的方法特别有用。


1
您能写更多有关“类型归因”的信息吗?它是什么以及它如何工作?
阿莫菲斯2011年


24

上面的所有答案看起来不错,但是只需要一个示例来解释这一点。这里是 :

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

所以现在我们知道:_*告诉编译器了:请解开这个参数并将它们绑定到函数调用中的vararg参数中,而不要把x作为一个参数。

因此,简而言之,:_*当将参数传递给vararg参数时,就是消除歧义。


5

对于像我这样的一些懒惰的人,它只是将Seq转换为varArgs!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.