Je suis un débutant à Haskell, alors soyez indulgent avec moi. (Je viens juste d'apprendre hier!) Comment puis-je trier une liste de tuples principalement par leurs premiers composants (du plus élevé au plus petit) et secondairement par leurs seconds composants (du plus petit au plus élevé)? Un exemple d'entrée/sortie serait:
[(1, "b"), (1, "a"), (2, "b"), (2, "a")]
(entrée)
[(1, "a"), (2, "a"), (1, "b"), (2, "b")]
(étape intermédiaire)
[(2, "a"), (2, "b"), (1, "a"), (1, "b")]
(sortie)
J'ai essayé d'utiliser ce qui suit mais cela a donné une sortie incorrecte:
sortGT a b = GT
sortBy sortGT lst
Je suis sûr que je peux le faire en utilisant sortBy
uniquement, mais je ne peux pas le comprendre moi-même. Toute aide serait très appréciée!
Vous devez construire votre fonction sortGT
, afin qu'elle compare les paires comme vous le souhaitez:
sortGT (a1, b1) (a2, b2)
| a1 < a2 = GT
| a1 > a2 = LT
| a1 == a2 = compare b1 b2
En utilisant cela, vous obtenez les résultats suivants (j'ai utilisé ghci):
*Main Data.List> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Puis-je suggérer ce qui suit?
import Data.List (sortBy)
import Data.Monoid (mconcat)
myPredicate (a1, a2) (b1, b2) = mconcat [compare b1 a1, compare a2 b2]
Vous pouvez ensuite trier en écrivant sortBy myPredicate lst
. La fonction mconcat
parcourt simplement la liste et obtient la première occurrence nonEQ
(ou EQ
si tous les éléments sont EQ
et donc les deux paires sont considérées égal).
À la réflexion, la construction de la liste n'est pas nécessaire.
import Data.List (sortBy)
import Data.Monoid (mappend)
myPredicate (a1, a2) (b1, b2) = compare b1 a1 `mappend` compare a2 b2
La définition de mappend
pour Ordering
est essentiellement:
EQ `mappend` x = x
x `mappend` _ = x
C'est exactement ce dont nous avons besoin.
Juste pour le plaisir, en généralisant la réponse de gbacon et en rendant l'utilisation un peu plus flexible:
import Data.Ord
import Data.List
import Data.Monoid
ascending = id
descending = flip
sortPairs f x g y = f (comparing x) `mappend` g (comparing y)
mySort = sortBy (sortPairs descending fst ascending snd)
D'abord, nous devons faire la fonction de classement qui prend deux fois et retourne EQ
, LT
ou GT
(ie. sortGT :: (a,b) -> (a,b) -> Ordering
.) Ensuite, nous pouvons donner cette fonction de classement à sortBy
et il triera son entrée en fonction de ce classement.
Puisque vous voulez que les premiers composants aient la première priorité, nous vérifions d'abord et s'ils sont égaux nous vérifions le deuxième argument, si les premiers composants ne sont pas égaux, nous lui donnons la valeur opposée de son ordre d'origine, de sorte qu'il soit ordonné le plus haut au plus bas.
C'est ce que je pense être le plus facile pour les yeux:
sortGT (a1,b1) (a2,b2) =
case compare a1 a2 of
EQ -> compare b1 b2
LT -> GT
GT -> LT
Maintenant, nous utilisons sortBy comme vous l'avez suggéré:
*Main> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Félicitations d'avoir fait vos premiers pas pour apprendre Haskell. C'est un grand voyage!
Riffing sur réponse de FredOverflow :
import Data.Ord
import Data.List
import Data.Monoid
main :: IO ()
main = do
print $ sortBy cmp [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
where
cmp = flip (comparing fst) `mappend` comparing snd
Production:
[(2, "a"), (2, "b"), (1, "a"), (1, "b")]
La solution suivante fonctionne le mieux pour moi, un débutant Haskell. Cela ressemble beaucoup à lectrologos 'réponse: en fait, j'ai seulement ajouté la définition de fonction et l'importation de liste, mais cela peut causer une certaine confusion s'il est omis.
Créez une fonction 'myCompare' et n'oubliez pas d'importer le module List. Vous en aurez besoin pour que le tri fonctionne. La fonction devrait ressembler à ceci:
import Data.List
myCompare :: (Ord a, Ord b) => (a,b) -> (a,b) -> Ordering
myCompare (a1,b1) (a2,b2)
| a1 < a2 = GT
| a2 == a1 = EQ
| otherwise = LT
Après avoir chargé le fichier Haskell, vous pouvez écrire ce qui suit dans votre terminal:
*Main> sortBy myCompare [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
Qui reviendra:
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
J'aime la fonction de comparaison dans Data.Ord. C'est fondamentalement la réponse de Greg sous une forme encore plus compacte:
Prelude Data.Ord Data.List Data.Function> (reverse.sortBy (comparing fst)) [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2, "a"), (2, "b"), (1, "a"), (1, "b")]
"compare fst" donne un ordre basé sur le premier élément du Tuple.
Il est toujours difficile de composer des fonctions à deux arguments. Voici une implémentation:
invert :: Ordering -> Ordering
invert GT = LT
invert LT = GT
invert EQ = EQ
sosort :: (Ord a, Ord b) => [(a, b)] -> [(a, b)]
sosort = sortBy (\p p' -> invert $ uncurry compare $ double fst p p') .
sortBy (\p p' -> uncurry compare $ double snd p p')
where double f a a' = (f a, f a')
Parce que sortBy
attend une fonction de deux arguments, la composition de la fonction n'est pas si agréable.
J'ai testé ce code et il fonctionne sur votre exemple.
Comme le souligne Fred, vous pouvez écrire compare EQ
au lieu de invert
. Comme le fait remarquer Dario, je pourrais utiliser on
de Data.Function
, mais en fait on compare == comparing
, que je peux utiliser à la place. Maintenant, le code ne peut être lu que par un maître Haskell:
sosort :: (Ord a, Ord b) => [(a, b)] -> [(a, b)]
sosort = sortBy (compare EQ `post` comparing fst) . sortBy (comparing snd)
where post f g x x' = f (g x x')
J'ai compilé et exécuté ce code et cela fonctionne sur l'exemple d'origine.
(Je n'ai pas de vote pour cette réponse, mais grâce à de bons commentaires, j'ai certainement beaucoup appris sur la bibliothèque Haskell. Qui sait à quoi correspond la fonction post
? Pas Hoogle ...)
Ce serait plus idiomatique pour écrire une fonction de comparaison appropriée pour les paires, mais votre question demandait des tris consécutifs.