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

时间:2011-05-18 21:35:52

标签: scala pattern-matching

我有this question的以下代码:

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连接,然后呢? : _*做了什么?

4 个答案:

答案 0 :(得分:133)

它“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的可变数量的参数。也就是说,如果方法是m类型     (p1:T1,...,pn:Tn,ps:S *)U应用于参数(e1,...,ek),其中k> = n,则     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])

  

答案 1 :(得分:83)

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

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

答案 2 :(得分:13)

以上所有答案看起来都很棒,但只需要一个样本来解释这一点。 这是:

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参数时消除歧义。

答案 3 :(得分:1)

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