2016-05-21 28 views
1

我試圖實現一個排序的多屬性排序,適用於任何列表。Haskell:多屬性排序不夠通用

import Data.Ord (Ordering, Down (..), comparing) 
import Data.List (sortBy) 
import Data.Monoid (mconcat) 

data Order a = ASC a | DESC a 

orderBy :: Ord b => [Order (a -> b)] -> [a] -> [a] 
orderBy rankedProperties unsorted = 
    sortBy rankedCompare unsorted 
    where 
     rankedCompare x y = 
      mconcat $ map 
       (\property -> 
        case property of 
         ASC f -> comparing f x y 
         DESC f -> comparing (Down . f) x y 
       ) rankedProperties 

它現在適用於元組和記錄,但是我發現了一個問題。問題是orderBy中的b必須相同。這是考慮這個問題:

data Row = Row { shortListed :: Bool, cost :: Float, distance1 :: Int, distance2 :: Int } deriving (Show, Eq) 

我希望能夠說:orderBy [ASC shortListed, DESC cost] listofrows

但是回來的錯誤是:

<interactive>:1:31: 
    Couldn't match type ‘Float’ with ‘Bool’ 
    Expected type: Row -> Bool 
     Actual type: Row -> Float 
    In the first argument of ‘ASC’, namely ‘cost’ 
    In the expression: ASC cost 

我需要一種方法來使b型通用,爲b只有真正必須由comparing功能comparing :: Ord a => (b -> a) -> b -> b -> Ordering接受。

我讀了一點關於存在類型和異構列表,但我不知道如何繼續。

回答

5

由於我們有Monoid Orderinginstance Monoid b => Monoid (a -> b)Prelude,我們也有Monoid (a -> a -> Ordering)迭代函數實例兩次。這讓我們解決這個問題很簡單,沒有existentials:

import Data.Ord (Ordering, comparing) 
import Data.List (sortBy) 
import Data.Monoid ((<>), mconcat) 

data Row = Row { 
    shortListed :: Bool, 
    cost :: Float, 
    distance1 :: Int, 
    distance2 :: Int 
    } deriving (Show, Eq) 

asc, desc :: Ord b => (a -> b) -> a -> a -> Ordering 
asc = comparing 
desc = flip . asc 

list :: [Row] 
list = [Row False 0 10 20, Row True 10 30 40] 

list' :: [Row] 
list' = sortBy (asc shortListed <> desc cost <> asc distance1) list 

或者:

orderBy :: [a -> a -> Ordering] -> [a] -> [a] 
orderBy = sortBy . mconcat 

list'' :: [Row] 
list'' = orderBy [asc shortListed, desc cost, asc distance1] list 
+0

我想的只是接受,能產生'Ordering'的功能,但我認爲它會產生太多的噪音與重複「比較」。然而,我沒有意識到只是將'比較'包裝到升序和降序構造函數中。 – CMCDragonkai