ChatGPT解决这个技术问题 Extra ChatGPT

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

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

非常感谢您在标题中添加(冒号下划线星号)!

N
Nimantha

它“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 ¡ n 类型的 S 出现,其中 ps 之外的任何参数名称都是新的。此规则的唯一例外是如果最后一个参数通过 _ 类型注释标记为序列参数。如果将上面的 m 应用于参数 (e1, . . . , en,e0 : _),则该应用程序中 m 的类型被视为 (p1 : T1, . . . ., pn : Tn,ps :scala.序列[S])**


我们喜欢称它为“Smooch 运算符”,即使它实际上不是运算符 :)
在 Python 中,这称为解包
序列的长度是否有限制,例如 Java 可变参数?
V
Vasil Remeniuk

child ++ newChild - 序列

- 类型归属,帮助编译器理解的提示,该表达式有什么类型

_* - 接受任何值的占位符 + 可变参数运算符

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


你能写更多关于“类型归属”的内容吗?它是什么以及它是如何工作的?
很好的答案。因此,通过编写 a: _*,您是在告诉编译器将 a 视为 _* 的一个实例,在本例中只是 Node*
K
Keith

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

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


这是我一直在寻找的答案。其他的都很棒,但是这个简单的例子让我很开心。给@Keith 的小费
m
mani_nz

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