使用重复参数的成本

时间:2010-03-16 03:00:00

标签: scala scala-2.8 variadic-functions argument-passing

我考虑重构一些方法签名,这些方法签名目前采用类型ListSet类型的参数 - List[Foo] - 来改为使用重复参数Foo*

  

更新以下推理存在缺陷,请继续......
  这将允许我使用相同的方法名称并根据参数类型重载它。使用ListSet无法做到这一点,因为List[Foo]List[Bar]在删除后具有相同的类型:List[Object]

在我的情况下,重构的方法与重复参数产生的scala.Seq[Foo]一起正常工作。我必须更改所有调用并将序列参数类型注释添加到所有集合参数:baz.doStuffWith(foos:_*)

鉴于从集合参数切换到重复参数在语义上是等效的,这个变化是否会产生一些我应该注意的性能影响?

scala 2.7._和2.8的答案是否相同?

3 个答案:

答案 0 :(得分:4)

当Scala调用Scala varargs方法时,该方法将接收扩展Seq的对象。当使用: _*进行调用时,对象将按 * 传递,而不进行复制。以下是这方面的例子:

scala> object T {
     |   class X(val self: List[Int]) extends SeqProxy[Int]  {
     |     private val serial = X.newSerial
     |     override def toString = serial.toString+":"+super.toString
     |   }
     |   object X {
     |     def apply(l: List[Int]) = new X(l)
     |     private var serial = 0
     |     def newSerial = {
     |       serial += 1
     |       serial
     |     }
     |   }
     | }
defined module T

scala> new T.X(List(1,2,3))
res0: T.X = 1:List(1, 2, 3)

scala> new T.X(List(1,2,3))
res1: T.X = 2:List(1, 2, 3)

scala> def f(xs: Int*) = xs.toString
f: (Int*)String

scala> f(res0: _*)
res3: String = 1:List(1, 2, 3)

scala> f(res1: _*)
res4: String = 2:List(1, 2, 3)

scala> def f(xs: Int*): Seq[Int] = xs
f: (Int*)Seq[Int]

scala> def f(xs: Int*) = xs match {
     |   case ys: List[_] => println("List")
     |   case _ => println("Something else")
     | }
f: (Int*)Unit

scala> f(List(1,2,3): _*)
List

scala> f(res0: _*)
Something else

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> def f(xs: Int*) = xs match {
     |   case ys: List[_] => println("List")
     |   case zs: ArrayBuffer[_] => zs.asInstanceOf[ArrayBuffer[Int]] += 4; println("Array Buffer")
     |   case _ => println("Something else")
     | }
f: (Int*)Unit

scala> val ab = new ArrayBuffer[Int]()
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> ab + 1
res11: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)

scala> ab + 2
res12: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2)

scala> ab + 3
res13: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)

scala> f(ab: _*)
Array Buffer

scala> ab
res15: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)

注意

  • Array作为WrappedArray传递。但是,不会复制所涉及的元素,WrappedArray的更改将反映在Array中。

答案 1 :(得分:3)

你用T *替换List [T]的原因是有缺陷的:Scala不允许重载像

class Foo
{
   def t1(x : Int*) = println("Ints")
   def t1(x : Strings*) = println("Strings")
}

这将导致与此处使用List [Int] / List [String]相同的编译器错误。

虽然你可以使用有点笨拙

class Foo
{
   def t1(x0 : Int,x : Int*) = println("Ints")
   def t1(x0 : String,x : Strings*) = println("Strings")
}

但这需要对第一个参数进行特殊处理,而不是其他参数。

的Gr。西尔维奥

答案 2 :(得分:0)

用最简单的术语来说,所有与重复形式参数相对应的参数,无论其来源如何,都必须复制到某种顺序集合中,以便呈现给方法。准确使用什么类型的序列的细节因Scala版本而异,并且可能与参数的来源有关。但无论这些细节如何,它都是O(n)操作,尽管每件物品的成本非常低。序列本身至少会有一个,有时甚至是更多的实例分配。

兰德尔舒尔茨