web-dev-qa-db-ja.com

Haskellでは、組み込みのsortBy関数を使用してペア(タプル)のリストをソートする方法を教えてください。

私はハスケルの初心者ですので、ご容赦ください。 (昨日の学習を始めたばかりです!)タプルのリストを主に最初のコンポーネント(最高から最小)で、次に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 を使用するだけでこれを実行できると確信していますが、自分ではわかりません。どんな助けでも大歓迎です!

37
eclipseNoob

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

以下を提案してもいいですか?

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

mappendOrderingの定義は基本的に次のとおりです。

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

最初に、2つのタプルを取り、EQLTまたは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")]
10
HaskellElephant

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

次の解決策は、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")]
1
Thijs Lowette

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の比較"は、タプルの最初の要素に基づいた順序を示します。

0
Tim

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.Functiononを使用することもできますが、実際には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ではありません...)

ペアの適切な比較関数を書く方がより慣用的ですが、あなたの質問は連続したソートを求めていました。

0
Norman Ramsey