2010-03-16 12 views
5

Ich halte Refactoring einige Methodensignaturen, die derzeit Parameter des Typs nehmen List oder Set von konkreten Klassen - List[Foo]-wiederholt Parameter stattdessen zu verwenden: Foo*.Kosten wiederholt Parameter der Verwendung

aktualisieren: Nach Argumentation ist fehlerhaft, entlang bewegen ...
Dies würde erlauben Sie mir die gleiche Methode Namen zu verwenden und es Überlastung basierend auf dem Parameter-Typ. Dies war unter Verwendung von List oder Set nicht möglich, da List[Foo] und List[Bar] denselben Typ nach dem Löschen haben: .

In meinem Fall funktionieren die refaktorierten Methoden gut mit scala.Seq[Foo], die sich aus dem wiederholten Parameter ergibt. Ich müsste alle Aufrufe ändern und ein Sequenzargument Typ Annotation zu allen Sammlungsparametern hinzufügen: baz.doStuffWith(foos:_*).

Vorausgesetzt, dass der Wechsel von Collection-Parameter zu wiederholten Parameter semantisch gleichwertig ist, hat diese Änderung einige Auswirkungen auf die Leistung, denen ich bewusst sein sollte?

Ist die Antwort für scala 2.7._ und 2.8 gleich?

Antwort

4

Wenn Scala ist ein Scala varargs Methode aufruft, wobei das Verfahren wird ein Objekt empfangen, das Seq erstreckt. Wenn der Anruf mit : _* getätigt wird, wird das Objekt wie * übergeben, ohne zu kopieren. Hier sind einige Beispiele dafür:

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) 

Hinweis

  • Ein Array als WrappedArray geben wird. Es gibt jedoch kein Kopieren von Elementen, und Änderungen an der WrappedArray werden sich in der Array widerspiegeln.
+0

"Wenn der Aufruf mit: _ * erfolgt, wird das Objekt wie folgt übergeben:" Es ist nicht wahr, wenn ein Array [T] übergeben wird. scala> f (Array (1,2,3,4): _ *) res1: String = WrappedArray (1, 2, 3, 4) – Eastsun

+0

@Eastsun Ah, ja, das 'Array'. Änderungen am 'WrappedArray' werden jedoch auch im' Array' reflektiert, so dass immer noch keine Elemente kopiert werden können. –

0

In den einfachsten Begriffen müssen alle Argumente, die einem wiederholten formalen Parameter entsprechen, unabhängig von ihrer Herkunft, in eine sequenzielle Sammlung einer Art für die Präsentation in der Methode kopiert werden. Die Einzelheiten darüber, welche Art von Sequenz verwendet wird, variieren mit der Scala-Version und möglicherweise mit der Quelle der Argumente. Aber unabhängig von diesen Details ist es eine Operation O (n), obwohl die Kosten pro Element ziemlich niedrig sind. Es wird mindestens eine und manchmal mehr Instanzzuweisungen für die Sequenz selbst geben.

Randall Schulz

+0

Das ist falsch. In mindestens 2.8, wenn Sie bereits eine Seq haben, wenn Sie es als varargs übergeben mit: _ * dann wird die Sammlung nicht kopiert, also ist es O (1), nicht O (n). –

3

Ihr Grund für den Ersatz Liste [T] mit T * ist fehlerhaft: Scala wird nicht zulassen, wie

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

Überlastung Diese im gleichen Compiler-Fehler führen wird, wie mit List [Int]/List [String ] Hier.

Obwohl er ein wenig ungeschickt Sie

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

verwenden könnte, aber das erfordert eine spezielle Behandlung des ersten Parameters im Vergleich zu den übrigen.

Gr. Silvio