Le coût de l'utilisation des paramètres répétés
-
20-09-2019 - |
Question
Je considère refactorisation quelques signatures de méthode qui prennent actuellement paramètre de type List
ou Set
des classes concrètes --List[Foo]
-- utiliser paramètres répétés à la place: Foo*
.
Mise à jour : Après raisonnement est erroné, circulez ... Cela me permettrait d'utiliser le même nom de la méthode et de surcharger en fonction du type de paramètre. Cela n'a pas été possible en utilisant
List
ouSet
, parceList[Foo]
etList[Bar]
ont même type après l'effacement.List[Object]
Dans mon cas, les méthodes refactorisé fonctionnent très bien avec scala.Seq[Foo]
qui résulte du paramètre répété. Je dois changer toutes les invocations et ajouter un argument séquence annotation de type à tous les paramètres de collecte. baz.doStuffWith(foos:_*)
Étant donné que le passage du paramètre de collection au paramètre répété est sémantiquement équivalent, ce que ce changement ont un certain impact sur les performances que je devrais être au courant?
La réponse est de même pour scala 2.7._ et 2.8?
La solution
Quand Scala appelle une méthode Scala varargs, la méthode reçoit un objet qui étend Seq
. Lorsque l'appel est fait avec : _*
, l'objet sera adopté tel quel * , sans copier. Voici les exemples:
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)
Remarque
- Un
Array
est passé comme unWrappedArray
. Il n'y a pas de copie d'éléments impliqués, cependant, et les changements auWrappedArray
seront pris en compte dans leArray
.
Autres conseils
Votre raison pour le remplacement Liste [T] avec T * est défectueux: Scala ne permettra pas à une surcharge comme
class Foo
{
def t1(x : Int*) = println("Ints")
def t1(x : Strings*) = println("Strings")
}
Cela se traduira par la même erreur du compilateur en utilisant la liste [Int] / Liste [chaîne] ici.
Bien qu'un peu maladroit, vous pouvez utiliser
class Foo
{
def t1(x0 : Int,x : Int*) = println("Ints")
def t1(x0 : String,x : Strings*) = println("Strings")
}
mais qui nécessite un traitement spécial du premier paramètre par rapport au reste.
Gr. Silvio
Dans les termes les plus simples, tous les arguments qui correspondent à un des paramètres formels répétées, quelle que soit leur origine, doivent être copiés dans une collection séquentielle de quelque sorte pour la présentation à la méthode. Les détails de exactement ce type de séquence est utilisée varient avec la version Scala et peut-être à la source des arguments. Mais indépendamment de ces détails, il est un O (n) exploitation, bien que le coût par article est assez faible. Il y aura au moins un et parfois plusieurs allocations d'instance pour la séquence elle-même.
Randall Schulz