В Haskell, как я могу использовать встроенную функцию sortBy для сортировки списка пар (кортеж)?
Я новичок в Haskell, поэтому, пожалуйста, несите меня. (Только что начал изучать вчера!) Как я могу отсортировать список кортежей в первую очередь по их первым компонентам (наивысшим до наименьшего), а во-вторых, по их вторым компонентам (от минимального до самого высокого)? Пример ввода/вывода будет:
[(1, "b"), (1, "a"), (2, "b"), (2, "a")]
(ввод)
[(1, "a"), (2, "a"), (1, "b"), (2, "b")]
(средний шаг)
[(2, "a"), (2, "b"), (1, "a"), (1, "b")]
(вывод)
Я попытался использовать следующее, но дал неверный вывод:
sortGT a b = GT
sortBy sortGT lst
Я уверен, что могу это сделать, используя sortBy
, но я не могу понять это сам. Любая помощь будет высоко оценена!
Ответы
Ответ 1
Вам нужно построить свою функцию sortGT
, чтобы она сравнивала пары так, как вы хотите:
sortGT (a1, b1) (a2, b2)
| a1 < a2 = GT
| a1 > a2 = LT
| a1 == a2 = compare b1 b2
Используя это, вы получаете следующие результаты (я использовал ghci):
*Main Data.List> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Ответ 2
Можно ли предложить следующее?
import Data.List (sortBy)
import Data.Monoid (mconcat)
myPredicate (a1, a2) (b1, b2) = mconcat [compare b1 a1, compare a2 b2]
Затем вы можете отсортировать, написав sortBy myPredicate lst
. Функция mconcat
просто просматривает список и получает первое событие не EQ
(или EQ
, если все элементы EQ
и, следовательно, обе пары считаются равными).
С другой стороны, создание списка не требуется.
import Data.List (sortBy)
import Data.Monoid (mappend)
myPredicate (a1, a2) (b1, b2) = compare b1 a1 `mappend` compare a2 b2
Определение mappend
для Ordering
по существу:
EQ `mappend` x = x
x `mappend` _ = x
Это именно то, что нам нужно.
Просто для удовольствия, обобщая ответ gbacon и делая использование более гибким:
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)
Ответ 3
Поздравляем вас с первыми шагами по изучению Haskell. Это отличная поездка!
Riffing on Ответ 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
Вывод:
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Ответ 4
Сначала мы должны сделать функцию упорядочения, которая берет два кортежа и возвращает либо EQ
, LT
, либо GT
(т.е. sortGT :: (a,b) -> (a,b) -> Ordering
.) Тогда мы можем дать эту функцию упорядочения sortBy
, и она будет сортируйте его в соответствии с этим порядком.
Поскольку вы хотите, чтобы первые компоненты имели первый приоритет, мы сначала проверяем это, и если они равны, мы проверяем второй аргумент, если первые компоненты не равны, мы даем ему противоположное значение его первоначального упорядочения, упорядочен с наивысшим наименьшим.
Это то, что я считаю самым легким на глазах:
sortGT (a1,b1) (a2,b2) =
case compare a1 a2 of
EQ -> compare b1 b2
LT -> GT
GT -> LT
Теперь мы используем sortBy, как вы сказали:
*Main> sortBy sortGT [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Ответ 5
Следующее решение работает лучше всего для меня, новичок Haskell. Он очень похож на 3lectrologos: я фактически добавил только определение функции и импорт List, но это может вызвать некоторую путаницу, если не учитывать.
Создайте функцию 'myCompare' и не забудьте импортировать модуль List. Вам понадобится, чтобы сделать sortBy. Функция должна выглядеть так:
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
После загрузки файла Haskell вы можете записать в своем терминале следующее:
*Main> sortBy myCompare [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
Что вернет:
[(2,"a"),(2,"b"),(1,"a"),(1,"b")]
Ответ 6
Это всегда сложно скомпоновать функции с двумя аргументами.
Вот реализация:
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')
Поскольку sortBy
ожидает функцию из двух аргументов, состав функций не так хорош.
Я тестировал этот код, и он работает на вашем примере.
Как указывает Фред, вы можете написать compare EQ
вместо invert
. Как указывает Дарио, я мог бы использовать on
из Data.Function
, но на самом деле on compare == comparing
, который я могу использовать вместо этого. Теперь код может быть прочитан только мастером 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')
Я скомпилировал и запустил этот код, и он работает на исходном примере.
(у меня нет голосов за этот ответ, но благодаря хорошим комментариям я, конечно, много узнал о библиотеке Haskell. Кто знает, какая функция post
эквивалентна? Не Hoogle...)
Было бы более идиоматично писать подходящую функцию сравнения для пар, но на ваш вопрос задавались последовательные сортировки.
Ответ 7
Мне нравится функция сравнения в Data.Ord. Это в основном ответ Грега в еще более компактной форме:
Prelude Data.Ord Data.List Data.Function> (reverse.sortBy (comparing fst)) [(1, "b"), (1, "a"), (2, "b"), (2, "a")]
[(2, "а" ), (2, "б" ), (1, "а" ), (1, "б" )]
"Сравнение fst" дает упорядочение на основе первого элемента кортежа.