83 votes

Produit cartésien de 2 listes en Haskell

Je souhaite réaliser le produit cartésien de 2 listes en Haskell, mais je n'arrive pas à trouver comment le faire. Le produit cartésien donne toutes les combinaisons des éléments de la liste :

xs = [1,2,3]
ys = [4,5,6]

cartProd :: [a] -> [b] -> [(a,b)]
cartProd xs ys ==> [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]

Il ne s'agit pas d'une vraie question de devoir et elle n'est pas liée à une telle question, mais la façon dont ce problème est résolu peut aider à résoudre un problème sur lequel je suis bloqué.

0 votes

134voto

sepp2k Points 157757

C'est très facile avec les compréhensions de listes. Pour obtenir le produit cartésien des listes xs y ys nous avons juste besoin de prendre le tuple (x,y) pour chaque élément x en xs et chaque élément y en ys .

Cela nous donne la liste de compréhension suivante :

cartProd xs ys = [(x,y) | x <- xs, y <- ys]

2 votes

Merci, c'est si simple mais élégant, ça a vraiment aidé pour l'autre problème :)

3 votes

Bon aussi pour Erlang, merci. Petit changement dans la syntaxe : cartProd(Xs, Ys) -> [{X,Y} || X <- Xs, Y <- Ys].

83voto

Travis Brown Points 56342

Comme d'autres réponses l'ont noté, l'utilisation d'une liste de compréhension est la façon la plus naturelle de le faire en Haskell.

Si vous apprenez Haskell et que vous souhaitez développer des intuitions sur les classes de types telles que Monad Cependant, c'est un exercice amusant que de comprendre pourquoi cette définition légèrement plus courte est équivalente :

import Control.Monad (liftM2)

cartProd :: [a] -> [b] -> [(a, b)]
cartProd = liftM2 (,)

Vous ne voudrez probablement jamais écrire cela dans du code réel, mais l'idée de base est quelque chose que vous verrez en Haskell tout le temps : nous utilisons liftM2 pour lever la fonction non monadique (,) dans une monade, dans ce cas précis la monade liste.

Si cela n'a aucun sens ou n'est pas utile, oubliez-le - c'est juste une autre façon d'aborder le problème.

4 votes

C'est probablement une bonne idée d'apprendre d'abord ce que font réellement les monades :P

23 votes

En guise de note de bas de page (trois ans plus tard) : Je devinerais maintenant que j'ai utilisé à l'origine la monade liftM2 ici pour des raisons de clarté (plus de gens ont entendu parler des monades que des foncteurs applicatifs ?), mais tout ce dont vous avez besoin est l'instance du foncteur applicatif pour les listes, donc liftA2 fonctionnera de manière équivalente.

62voto

newacct Points 42530

Si vos listes d'entrée sont du même type, vous pouvez obtenir le produit cartésien d'un nombre arbitraire de listes en utilisant sequence (en utilisant le List monade). Vous obtiendrez ainsi une liste de listes au lieu d'une liste de tuples :

> sequence [[1,2,3],[4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]

56voto

Landei Points 30509

Il existe une manière très élégante de le faire avec les foncteurs applicatifs :

import Control.Applicative

(,) <$> [1,2,3] <*> [4,5,6]
-- [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]

L'idée de base est d'appliquer une fonction sur des arguments "enveloppés", par ex.

(+) <$> (Just 4) <*> (Just 10)
-- Just 14

Dans le cas des listes, la fonction sera appliquée à toutes les combinaisons, il suffit donc de les "tupler" avec (,) .

Voir http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors ou (plus théorique) http://www.soi.city.ac.uk/~ross/papers/Applicative.pdf pour les détails.

5 votes

C'est plutôt cool de pouvoir développer le tuple selon les besoins : (,,) <$> [1..3] <*> [4..6] <*> [7..9]

12voto

Stuart Golodetz Points 12679

La bonne façon de procéder est d'utiliser les compréhensions de listes, comme d'autres personnes l'ont déjà souligné, mais si vous voulez le faire sans utiliser les compréhensions de listes pour quelque raison que ce soit, vous pouvez faire ceci :

cartProd :: [a] -> [b] -> [(a,b)]
cartProd xs [] = []
cartProd [] ys = []
cartProd (x:xs) ys = map (\y -> (x,y)) ys ++ cartProd xs ys

3 votes

Une méthode plus simple, sans compréhension des listes, est cartProd xs ys = xs >>= \x -> ys >>= \y -> [(x,y)]

5 votes

Au lieu de map (\y -> (x,y)) vous pouvez écrire map ((,) x) .

1 votes

@Chuck : Nice :) Cela fait un moment que j'utilise Haskell, c'est pourquoi j'opte pour des solutions simplistes...

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