`:_*`(冒号下划线星号)在 Scala 中有啥作用?

Posted

技术标签:

【中文标题】`:_*`(冒号下划线星号)在 Scala 中有啥作用?【英文标题】:What does `:_*` (colon underscore star) do in Scala?`:_*`(冒号下划线星号)在 Scala 中有什么作用? 【发布时间】:2011-08-28 10:07:28 【问题描述】:

我有以下来自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 连接在一起,然后呢? : _* 是做什么的?

【问题讨论】:

非常感谢您在标题中添加(冒号下划线星号)! 【参考方案1】:

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

【讨论】:

我们喜欢称它为“Smooch 运营商”,尽管它实际上并不是运营商 :) 在 Python 中这称为解包 序列的长度是否有限制,比如Java varargs有限制?【参考方案2】: child ++ newChild - 序列 : - 类型归属,帮助编译器理解的提示,该表达式有什么类型 _* - 接受任何值的占位符 + 可变参数运算符

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

【讨论】:

你能写更多关于“类型归属”的内容吗?它是什么以及它是如何工作的? ***.com/questions/2087250/… 很好的答案。因此,通过编写a: _*,您是在告诉编译器将a 视为_* 的一个实例,在这种情况下只是Node*【参考方案3】:

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

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 作为单个参数。

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

【讨论】:

这是我一直在寻找的答案。其他的都很棒,但是这个简单的例子让我很开心。向@Keith 致敬【参考方案4】:

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

【讨论】:

以上是关于`:_*`(冒号下划线星号)在 Scala 中有啥作用?的主要内容,如果未能解决你的问题,请参考以下文章

Scala之下划线的作用

一些特殊符号的英文翻译?

*::(星号双冒号)在Ruby中做了啥?

scala中常用但其他语言不常见的符号含义 - 心灵空谷幽兰 - 博客园

URL 编码与解码使用详解

Perl 星号符号有啥作用