6 votes

Classes de cas Scala dans les collections

Scénario : Je suis en train d'analyser un IL et je veux convertir une représentation basée sur une pile en un CFG par exemple.

Mon IL consiste en de multiples opérations comme PushInt(value), Pop etc. La question est maintenant de savoir quelle implémentation serait correcte en termes de Scala. J'aimerais utiliser des classes/objets de cas ou des extracteurs afin de pouvoir écrire du code alà

op match {
  case PushInt(x) => doSomethingWith x
  case Pop => ...
}

Maintenant le problème existe avec une séquence comme PushInt(1) :: PushInt(1) :: Pop :: Pop puisque PushInt(1) est égal à PushInt(1) et que je ne peux pas ajouter plusieurs opérations (égales) dans une collection. Cependant, je sais que je jette une information, à savoir la position dans le flux, mais celle-ci est implicitement stockée sous forme d'index dans la séquence.

  • Une solution possible est de remplacer la méthode hashCode et d'enfreindre les règles de equal/hashCode. Je ne suis pas vraiment satisfait de cette solution.

  • Une autre option est d'avoir un compteur de "temps de création" qui est stocké dans la base abstraite de sorte que case class PushInt(value: Int) extends AbstractOp(AbstractOp.nextIndex)

  • Utiliser des extracteurs, mais dans ce cas, il me manquera des fonctionnalités intéressantes comme l'implémentation de hashCode, equals, toString et, plus important encore, la vérification d'une correspondance exhaustive.

Ma question est donc maintenant de savoir comment modéliser ma structure en fonction de mes besoins. L'une des solutions possibles est-elle "correcte" en termes de Scala ?

5voto

Daniel C. Sobral Points 159554

Tout d'abord, abordons le problème de la recherche de l'instance exacte que vous souhaitez :

scala> trait AbstractOp
defined trait AbstractOp

scala> case class Pop() extends AbstractOp {
     |   override def equals(other: Any) = other match {
     |     case that: Pop => this eq that
     |     case _ => false
     |   }
     | }
defined class Pop

scala> case class PushInt(val i: Int) extends AbstractOp {
     |   override def equals(other: Any) = other match {
     |     case that: PushInt => this eq that
     |     case _ => false
     |   }
     | }
defined class PushInt

scala> val l = List(PushInt(1), PushInt(1), Pop(), Pop())
l: List[Product with AbstractOp] = List(PushInt(1), PushInt(1), Pop(), Pop())

scala> val op = l(1)
op: Product with AbstractOp = PushInt(1)

scala> println( l.indexOf( op ) )
1

Cela signifie, bien sûr, que PushInt(1) != PushInt(1) sauf s'il s'agit exactement de la même instance de PushInt(1) . Il ne se casse pas equals / hashCode contrat car a.equals(b) => a.hashCode == b.hashCode mais a.hashCode == b.hashCode n'implique rien. Mais si votre seule utilisation est trouver dans ce cas, essayez plutôt ceci :

scala> case class Pop() extends AbstractOp
defined class Pop

scala> case class PushInt(val i: Int) extends AbstractOp
defined class PushInt

scala> val l = List(PushInt(1), PushInt(1), Pop(), Pop())
l: List[Product with AbstractOp] = List(PushInt(1), PushInt(1), Pop(), Pop())

scala> val op = l(1)
op: Product with AbstractOp = PushInt(1)

scala> println( l.findIndexOf( op eq _ ) )
1

Dans tous les cas, si vous réinsérez cette instance dans la liste, vous aurez des problèmes. Vous devez vous assurer que chaque instance que vous insérez est unique. Vous pouvez même écrire votre propre collection, soit en lançant une exception si une instance répétée est insérée, soit en faisant une copie de toute instance qui lui est passée (assez facile avec les classes de cas et les classes de copy sur Scala 2.8).

2voto

Patrick Points 12559

Si Joa n'y voit pas d'inconvénient ;) Imaginez un code comme celui-là :

trait AbstractOp
case class Pop() extends AbstractOp
case class PushInt(val i:Int) extends AbstractOp

nous construisons maintenant une liste représentant une séquence d'instructions d'un programme

val l=List(PushInt(1), PushInt(1), Pop(), Pop())

Premier problème : vous voulez obtenir l'index d'une opération

val op=l(1) // get the second operation for example
// now you want to get back the index for the op you are using
println( l.indexOf( op1 ) ) // you will get 0 and not 1

Deuxième problème : si vous voulez faire correspondre chaque opération de la liste précédente à une valeur, cela échouera puisque equals ne distinguera pas les deux Pop, ou les deux PushInt.

P.S. Bien sûr, ce n'est pas une réponse, je n'ai pas trouvé comment poster ceci sous les autres commentaires. N'hésitez pas à le déplacer au bon endroit

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X