Стоимость использования повторных параметров - PullRequest
5 голосов
/ 16 марта 2010

Я рассматриваю рефакторинг нескольких сигнатур методов, которые в настоящее время принимают параметр типа List или Set конкретных классов - List[Foo] - для использования повторяющихся параметров вместо: Foo*.

Обновление : Следуя рассуждениям с ошибками, двигайтесь дальше ...
Это позволило бы мне использовать одно и то же имя метода и перегрузить его в зависимости от типа параметра. Это было невозможно при использовании List или Set, потому что List[Foo] и List[Bar] имеют один и тот же тип после стирания: List[Object].

В моем случае рефакторинговые методы работают нормально с scala.Seq[Foo], что является результатом повторного параметра. Я должен был бы изменить все вызовы и добавить последовательность аргументов примечание типа ко всем параметрам коллекции: baz.doStuffWith(foos:_*).

Учитывая, что переключение с параметра коллекции на повторный параметр семантически эквивалентно, оказывает ли это изменение некоторое влияние на производительность, о котором я должен знать?

Является ли ответ одинаковым для scala 2.7._ и 2.8?

Ответы [ 3 ]

4 голосов
/ 16 марта 2010

Когда 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.
3 голосов
/ 16 марта 2010

Ваша причина замены List [T] на 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. Сильвио

0 голосов
/ 16 марта 2010

Проще говоря, все аргументы, которые соответствуют повторяющимся формальным параметрам, независимо от их происхождения, должны быть скопированы в последовательную коллекцию некоторого вида для представления методу. Детали того, какая именно последовательность используется, зависит от версии Scala и, возможно, от источника аргументов. Но независимо от этих деталей, это операция O (n), хотя стоимость одного элемента довольно низкая. Для самой последовательности будет хотя бы одно, а иногда и большее количество экземпляров.

Рэндалл Шульц

...