私はハスケルの初心者ですので、ご容赦ください。 (昨日の学習を始めたばかりです!)タプルのリストを主に最初のコンポーネント(最高から最小)で、次に2番目のコンポーネント(最低から最高)でソートするにはどうすればよいですか?入力/出力の例は次のとおりです。
[(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
を使用するだけでこれを実行できると確信していますが、自分ではわかりません。どんな助けでも大歓迎です!
関数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")]
以下を提案してもいいですか?
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)
最初に、2つのタプルを取り、EQ
、LT
またはGT
を返す順序付け関数を作成する必要があります(つまり、sortGT :: (a,b) -> (a,b) -> Ordering
)。次に、この順序付け関数をsortBy
に変換すると、この順序付けに従って入力がソートされます。
最初のコンポーネントを最優先にしたいので、最初にそれをチェックし、それらが等しい場合は2番目の引数をチェックします。最初のコンポーネントが等しくない場合は、元の順序の反対の値を与えて、それが最も高い順序になるようにします。最低に。
これは私が目に最も簡単だと思うものです:
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")]
Haskellを学ぶための最初のステップを実行したことをお祝いします。それは素晴らしい旅です!
リフ 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")]
次の解決策は、Haskellの初心者である私に最適です。 lectrologos 'の回答によく似ています。実際には関数定義とリストインポートのみを追加しましたが、省略した場合は混乱が生じる可能性があります。
関数 '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")]
Data.Ordの比較機能が好きです。これは基本的にはさらにコンパクトな形式でのGregの答えです。
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")]
"fstの比較"は、タプルの最初の要素に基づいた順序を示します。
2つの引数を持つ関数を作成するのは常にトリッキーです。ここに実装があります:
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
は2つの引数の関数を想定しているため、関数の構成はそれほど良くありません。
私はこのコードをテストしましたが、あなたの例で動作します。
フレッドが指摘するように、invert
の代わりにcompare EQ
と書くことができます。 Darioが指摘するように、Data.Function
のon
を使用することもできますが、実際には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ではありません...)
ペアの適切な比較関数を書く方がより慣用的ですが、あなたの質問は連続したソートを求めていました。