11 votes

Scala - Renforcer la taille d'un vecteur au moment de la compilation

Est-il possible d'imposer la taille d'un Vector transmis à une méthode au moment de la compilation ? Je souhaite modéliser un espace euclidien à n dimensions à l'aide d'une collection de points dans l'espace qui ressemble à quelque chose comme ceci (c'est ce que j'ai maintenant) :

case class EuclideanPoint(coordinates: Vector[Double]) {
  def distanceTo(desination: EuclieanPoint): Double = ???
}

Si j'ai une coordonnée qui est créée via EuclideanPoint(Vector(1, 0, 0)) il s'agit d'un point euclidien en 3D. Compte tenu de cela, je veux m'assurer que le point de destination transmis lors d'un appel à distanceTo est de la même dimension.

Je sais que je peux le faire en utilisant Tuple1 a Tuple22 mais je veux représenter de nombreux espaces géométriques différents et j'écrirais 22 classes pour chaque espace si je le faisais avec Tuple existe-t-il une meilleure façon de procéder ?

12voto

Travis Brown Points 56342

Il est possible de le faire de plusieurs manières qui ressemblent toutes plus ou moins à ce que Randall Schulz a décrit dans un commentaire. Les Bibliothèque sans forme fournit une implémentation particulièrement pratique, qui vous permet d'obtenir quelque chose d'assez proche de ce que vous voulez comme ceci :

import shapeless._

case class EuclideanPoint[N <: Nat](
   coordinates: Sized[IndexedSeq[Double], N] { type A = Double }
) {
  def distanceTo(destination: EuclideanPoint[N]): Double = 
    math.sqrt(
      (this.coordinates zip destination.coordinates).map {
        case (a, b) => (a - b) * (a - b)
      }.sum
    )
}

Vous pouvez maintenant écrire ce qui suit :

val orig2d = EuclideanPoint(Sized(0.0, 0.0))
val unit2d = EuclideanPoint(Sized(1.0, 1.0))

val orig3d = EuclideanPoint(Sized(0.0, 0.0, 0.0))
val unit3d = EuclideanPoint(Sized(1.0, 1.0, 1.0))

Et.. :

scala> orig2d distanceTo unit2d
res0: Double = 1.4142135623730951

scala> orig3d distanceTo unit3d
res1: Double = 1.7320508075688772

Mais non :

scala> orig2d distanceTo unit3d
<console>:15: error: type mismatch;
 found   : EuclideanPoint[shapeless.Nat._3]
 required: EuclideanPoint[shapeless.Nat._2]
              orig2d distanceTo unit3d
                                ^

Sized est doté d'un certain nombre de fonctionnalités intéressantes, notamment une poignée d'opérations sur les collections qui comportent des garanties statiques sur la longueur. Nous pouvons par exemple écrire ce qui suit :

val somewhere = EuclideanPoint(Sized(0.0) ++ Sized(1.0, 0.0))

Et avoir un vieux point ordinaire dans l'espace tridimensionnel.

2voto

jroesch Points 760

Vous pourriez faire quelque chose vous-même en faisant un encodage au niveau du type des nombres naturels, comme par exemple : http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/ . Il suffit alors de paramétrer votre vecteur par un naturel. Cela ne nécessiterait pas de dépendance supplémentaire, mais serait probablement plus compliqué que d'utiliser Shapeless.

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