Bonjour à tous,
Récemment je me suis remis à apprendre la langage Haskell. En guise d'exercice j'ai essayé d'implémenter des vecteurs à 2 et 3 dimensions dans le but d'écrire un raytracer vraiment basique (les exemples pullulent sur le net). Bien que ce soit un cas assez basique, il y a à mon sens plusieurs manières de représenter un type Vecteur à 2 ou 3 dimensions, j'ai choisi d'utiliser une typeclass étant donné que beaucoup d'opérations sur les vecteurs à 2 et 3 dimensions se ressemblent.
J'ai donc plusieurs questions à poser à des Haskelleux plus expérimentés sur la pertinence d'une telle implémentations. J'écris mes questions un peu en vrac, vous trouverez le code à la fin du post.
1) J'utilise ici des tuples plutôt que des listes (contrairement à ce qu'on peut trouver ici). Est-ce que c'est plus idiomatique de faire de cette façon ? L'implémentation avec des listes est certes plus sexy parce qu'on peut facilement itérer dessus à coup de map ou zipWith, mais est-ce que cela ne pose pas des problèmes de "sécurité" par la suite ? Avec des tuples je suis sûr de la dimension des données que je manipule alors que ce n'est pas le cas avec des listes.
2) Je préfère utiliser un type paramétrique pour définir les vecteurs, ainsi j'ai plus de liberté par la suite si je veux manipuler autre chose que des instances de Num. Qu'en pensez-vous ?
3) Qu'en est-il des foncteurs ? Il y aurait t-il des avantages à représenter des vecteurs sous forme de liste et d'utiliser un foncteur pour les manipuler ?
Je suis désolé si les questions sembles confuses, n'hésitez pas à donner votre commentaire sur le code, tout est bon à prendre !
Merci d'avance
Le code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | module Vectors where import Prelude hiding ((*>), (<*>)) data Vector2D a = Vect2D (a, a) deriving (Show, Eq) data Vector3D a = Vect3D (a, a, a) deriving (Show, Eq) class Vector v where -- Basic operations (<+>) :: Num a => v a -> v a -> v a -- u+v (<->) :: Num a => v a -> v a -> v a -- u-v u <-> v = u <+> neg v (*>) :: Num a => a -> v a -> v a -- k*v k *> v = appEach (*k) v neg :: Num a => v a -> v a -- -v neg v = (-1) *> v (<.>) :: Num a => v a -> v a -> a -- u.v -- Norms and distances normalize :: (Floating a, Eq a) => v a -> v a normalize v = case norm v of 0 -> 0 *> v n -> (1/n) *> v mag :: Num a => v a -> a mag v = v <.> v norm :: Floating a => v a -> a norm = sqrt . mag mkNormedVect :: (Floating a, Eq a) => v a -> v a -> v a mkNormedVect a b = normalize $ b <-> a dist :: Floating a => v a -> v a -> a dist u v = norm $ v <-> u -- Apply f to each component appEach :: (a -> b) -> v a -> v b instance Vector Vector3D where Vect3D (a, b, c) <+> Vect3D (x, y, z) = Vect3D (a+x, b+y, c+z) Vect3D (a, b, c) <.> Vect3D (x, y, z) = a*x + b*y + c*z appEach f (Vect3D (x, y, z)) = Vect3D (f x, f y, f z) instance Vector Vector2D where Vect2D (a, b) <+> Vect2D (x, y) = Vect2D (a+x, b+y) Vect2D (a, b) <.> Vect2D (x, y) = a*x + b*y appEach f (Vect2D (x, y)) = Vect2D (f x, f y) -- u*v (<*>) :: Num a => Vector3D a -> Vector3D a -> Vector3D a Vect3D (a, b, c) <*> Vect3D (x, y, z) = Vect3D (b*z - c*y, c*x - a*z, a*y - b*x) |