59 votes

En Scala, existe-t-il un moyen simple de convertir une classe de cas en un tuple ?

Existe-t-il un moyen simple de convertir une classe de cas en un tuple ?

Je peux, bien sûr, facilement écrire un code passe-partout pour faire cela, mais je veux dire sans passe-partout.

Ce que je cherche vraiment, c'est un moyen de rendre facilement une classe de cas lexicographiquement ordonnée. Je peux atteindre cet objectif pour les tuples en important scala.math.Ordering.Implicits._, et voilà, mes tuples ont un ordre défini pour eux. Mais les implicites de scala.math.Ordering ne fonctionnent pas pour les classes de cas en général.

88voto

S-C Points 780

Et si vous appeliez unapply().get dans l'objet compagnon ?

case class Foo(foo: String, bar: Int)

val (str, in) = Foo.unapply(Foo("test", 123)).get
// str: String = test
// in: Int = 123

5 votes

Merci pour les conseils, dans le REPL 2.9.0-1 j'ai dû enlever le () pour "obtenir".

0 votes

Cool, ça a marché ! Avec votre suggestion, j'ai pu obtenir ma classe de cas ordonnée comme ceci : val thisTuple: Ordered[(String, Int)] = Foo.unapply(this).get; val thatTuple = Foo.unapply(that).get; thisTuple compare thatTuple . Ce n'est pas la plus belle chose du monde, je suis donc encore ouvert aux suggestions, mais votre réponse fait sûrement l'affaire. Merci !

0 votes

C'est assez lourd, et ce n'est pas vraiment une amélioration par rapport à la simple mise en place d'une solution standard.

6voto

James Moore Points 3293

Sans forme le fera pour vous.

  import shapeless._
  import shapeless.syntax.std.product._

  case class Fnord(a: Int, b: String)

  List(Fnord(1, "z - last"), Fnord(1, "a - first")).sortBy(_.productElements.tupled)

Obtenir

res0: List[Fnord] = List(Fnord(1,a - first), Fnord(1,z - last))

productElements transforme une classe de cas en une HList sans forme :

scala> Fnord(1, "z - last").productElements
res1: Int :: String :: shapeless.HNil = 1 :: z - last :: HNil

Et les HLists sont converties en tuples avec #tupled :

scala> Fnord(1, "z - last").productElements.tupled
res2: (Int, String) = (1,z - last)

Les performances risquent d'être horribles, puisque vous êtes constamment en train de convertir. Il est probable que vous convertissiez tout sous forme de tupled, que vous le triiez, puis que vous le reconvertissiez en utilisant quelque chose comme (Fnord.apply _).tupled .

3voto

Dean Wampler Points 960

Vous pouvez essayer d'étendre le ProductN trait, pour N=1-22, qui TupleN s'étend. Il vous donnera une grande partie de la sémantique des Tuple, comme la fonction _1 , _2 etc. Selon la façon dont vous utilisez vos types, cela peut être suffisant sans créer un Tuple réel.

0 votes

Je ne sais pas comment faire pour que cette approche fonctionne pour moi. Je veux que ma classe de cas soit ordonnée lexicographiquement ou qu'elle ait un ordre lexicographique, sans avoir à écrire beaucoup de texte passe-partout. L'extension de ProductN ne semble pas fonctionner avec les implicites de scala.math.Ordering qui permettent de donner facilement aux tuples un ordre lexicographique.

3voto

AnthonyShipman Points 88

Je suis tombé sur ce vieux fil de discussion en essayant de faire la même chose. J'ai finalement opté pour cette solution :

case class Foo(foo: String, bar: Int)

val testFoo = Foo("a string", 1)

val (str, in) = testFoo match { case Foo(f, b) => (f, b) }

1 votes

J'aime cette approche car je peux éviter d'appeler .get sur le Option renvoyé par unapply . Je me demande pourquoi une classe de cas n'a pas une méthode appelée .toTuple qui renvoie le tuple littéral, et non pas dans un Option c'est-à-dire ce que vous faites littéralement. Pour . unapply sur une instance de classe de cas, le résultat sera toujours un 'Some', ce qui m'oblige à appeler .get ce qui est une odeur de code.

1 votes

"Je peux, bien sûr, facilement écrire un code standard pour faire cela, mais je veux dire sans le code standard". - cette réponse ne répond pas à ce critère, et est en fait plus longue que la simple réponse de la Commission. (testFoo.foo, testFoo.bar)

3voto

Taig Points 1457

Scala 3 offre un support de premier ordre pour les conversions entre les classes de cas et les tuples :

scala> case class Foo(foo: String, bar: Int)
// defined case class Foo

scala> Tuple.fromProductTyped(Foo("a string", 1))
val res0: (String, Int) = (a string,1)

scala> summon[deriving.Mirror.ProductOf[Foo]].fromProduct(res0)
val res1: deriving.Mirror.ProductOf[Foo]#MirroredMonoType = Foo(a string,1)

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