2

コンパレータのリストを受け入れ、最初のコンパレータを使用して値のペアを比較するコンパレータを返し、最初のコンパレータが返された場合は2番目のコンパレータを返す関数を作成しようとしていますEQ

私が思いついたのは次の機能でした:

import Data.Monoid

chainCompare :: [a -> a -> Ordering] -> a -> a -> Ordering
chainCompare = mconcat . map ($)

編集:次chainCompareのように書くこともできます(指摘してくれたVitusに感謝します):

chaincompare = mconcat

この関数の使用例は次のとおりです。

import Data.List
import Data.Ord

sortBy (chainCompare [comparing length, comparing sum]) [[1..100], [1..20], [100..200]]

ただし、この関数は比較を明示的に使用する必要があるため、次のように関数を変更しようとしました。

chainCompare :: Ord b => [a -> b] -> a -> a -> Ordering
chainCompare = mconcat . map (comparing $)

ただし、chainCompareこの場合はコンパイル エラーが発生します (また、この例がコンパイルされたとしても、空の文字列では機能しません)。

 sortBy (chainCompare [length, head]) [['a'..'z'], ['A'..'Z'], "Lorem ipsum dolor sit amet"]

任意の型にできるchainCompareという意味でポリモーフィックにすることは可能ですか? 拡張機能を使用している Haskell コードをいくつか見て、それらを検索しようとしましたが、それぞれの特定の拡張機能が何に役立つのかまだわかりません。binstance Ordforall

4

1 に答える 1

7

chainCompare [f, g]fと が異なる型の関数である場合、gをどのように定義しても、常にエラーが発生しますchainCompare。を削除してchainCompare書き込むこともできますが[f, g]、それでもエラーが発生します。その理由は、リストに異なる型の値を持つことは単純に不可能だからです。

異なる型の値を同じリストに格納したい場合、存在型 (GHC 拡張) を使用することが理にかなっている場合があります。これにより、存在型Comparatorを定義して list を使用できます[Comparator length, Comparator head]。ただしcomparing、最初の例で行ったように使用するよりもメリットがないため、この場合はまったく無意味です。

したがって、最初のコードを使用comparingすることは、実際にできる最善の方法です。

記録として、存在型を使用したコードは次のようになります。

{-# LANGUAGE ExistentialQuantification #-}

import Data.Monoid
import Data.Ord

data Comparator a = forall b. Ord b => Comparator (a -> b)

chainCompare :: [Comparator a] -> a -> a -> Ordering
chainCompare = mconcat . map comp
  where comp (Comparator f) = comparing f

-- Usage:
list = [['a'..'z'], ['A'..'Z'], "Lorem ipsum dolor sit amet"]
sortedList = sortBy (chainCompare [Comparator length, Comparator head]) list

最初のバージョンとの使用方法の唯一の違いは、Comparator代わりに記述する必要がcomparingあることと、キーに基づいて比較しない比較関数を使用できないことです。私が言ったように、それはあなたの最初のバージョンよりも何の利益も追加しません.

于 2013-02-13T00:20:11.613 に答える