web-dev-qa-db-fra.com

Dans Haskell, comment puis-je utiliser la fonction intégrée sortBy pour trier une liste de paires (Tuple)?

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!

37
eclipseNoob

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")]
38
3lectrologos

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)
20
fredoverflow

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")]
10
HaskellElephant

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")]
8
Greg Bacon

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")]
1
Thijs Lowette

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.

0
Tim

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.

0
Norman Ramsey