在Scala中,param: _*是什么意思?

123

我是 Scala (2.9.1) 的新手,我有一个 List[Event] 并且想将它复制到一个 Queue[Event] 中,但是以下语法会得到一个 Queue[List[Event]]

val eventQueue = Queue(events)

由于某些原因,以下代码可以正常工作:

val eventQueue = Queue(events : _*)

但是我想了解它的作用和为什么它有效?我已经查看了Queue.apply函数的签名:

def apply[A](elems: A*)

我明白第一次尝试为什么不起作用,但是第二个尝试的含义是什么?在这种情况下,:_*是什么意思,为什么apply函数不只接受一个Iterable[A]

3个回答

117

a: A 是类型注释;参见在Scala中类型注释的目的是什么?

: _* 是类型注释的特殊实例,告诉编译器将序列类型的单个参数视为可变参数序列,即varargs。

使用Queue.apply创建只包含一个序列或可迭代元素的Queue是完全有效的,因此当您提供单个Iterable[A]时,就会发生这种情况。


所以:a: _* 就像 JavaScript 中的展开运算符 ...a,其中 a 是一个集合。您可以在此处查看展开运算符 -> https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax - Victor

101

这是一种特殊的注释,告诉编译器将每个元素作为自己的参数传递,而不是作为单个参数全部传递。请参见此处

它是一种类型注释,指示序列参数,并在语言规范的第4.6.2节“重复参数”中被提及作为“例外”的情况。

当函数接受可变数量的参数时非常有用,例如像def sum(args: Int*)这样的函数,可以被调用为sum(1)sum(1,2)等。如果您有一个列表,例如xs = List(1,2,3),您不能传递xs本身,因为它是一个List而不是Int,但是您可以使用sum(xs: _*)来传递它的元素。


def sum(xs: _*) 抛出 'error: 未绑定通配符类型'。 - 7kemZmani
你的回答很清晰,但实际上这让我更加困惑了。通常在Scala中,xs: int表示xs的类型是int,按照这个理解,上面的语法中xs: _*是否意味着将xs强制转换为其各个成员? - Rpant
跟随上面的链接看起来就是这样,类型注释是 Scala 中用于 Java 类型转换的术语。如果有错误,请纠正我。 - Rpant
3
@7kemZmani:你必须使用特定的可变参数类型来定义函数:def sum(args: Int*),然后使用通配符“泛型”可变参数类型进行调用:val a = sum(xs: _*)。将 _* 视为“我正在传递一个在方法签名中定义的 Int、String 或任何其他类型的参数”。 - Alfonso Nishikawa

23

对于Python开发者:

Scala的_*运算符或多或少相当于Python的星号运算符


示例

将来自链接提供的Scala示例转换为Python,参见Luigi Plinge

def echo(args: String*) = 
    for (arg <- args) println(arg)

val arr = Array("What's", "up", "doc?")
echo(arr: _*)

转换成 Python 的代码如下:

def echo(*args):
    for arg in args:
        print "%s" % arg

arr = ["What's", "up", "doc?"]
echo(*arr)

两者都会输出以下内容:

What's
up
doc?


区别:展开位置参数

虽然Python的*操作符也可以处理定长函数的位置参数/参数的展开:

def multiply (x, y):
    return x * y

operands = (2, 4)
multiply(*operands)

8

用Scala进行相同的操作:

def multiply(x:Int, y:Int) = {
    x * y;
}

val operands = (2, 4)
multiply (operands : _*)

将会失败:

方法 multiply 缺少足够的参数: (x: Int, y: Int)Int。
未指定值参数 y。

但是可以用Scala实现相同的功能:

def multiply(x:Int, y:Int) = {
    x*y;
}

val operands = (2, 4)
multiply _ tupled operands

根据 Lorrin Nelson 的说法,它是这样工作的:

f _ 是部分应用函数的语法,其中没有指定任何参数。这是获取函数对象的机制。 tupled 返回一个新的函数,它是具有单个元组参数的元数 - 1 函数。

更多阅读:


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