2013-02-13 57 views
1

我想寫接受比較的列表,並返回一個比較,如果第一比較返回EQ如何編寫比較器鏈接函數?

將比較一對使用第一比較,然後第二個值的函數我想出了以下功能:

import Data.Monoid 

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

編輯:chainCompare也可以寫爲(感謝維圖斯指點出來):

chaincompare = mconcat 

的examp使用此功能的文件如下:

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多態在這個意義上說,b可以是任何類型的instance Ord?我已經看到一些使用forall擴展的Haskell代碼,並嘗試搜索它們,但我仍然無法弄清每個特定擴展的用處。

+2

'map($)= map id = id'(具有更多特殊類型),您的第一個'chainCompare'只是'mconcat'。 – Vitus 2013-02-13 00:23:56

+0

@Vitus感謝您指出 – Alexandros 2013-02-13 00:31:26

回答

6

chainCompare [f, g]將始終導致錯誤,如果fg是不同類型的函數 - 無論您如何定義chainCompare。你甚至可以刪除chainCompare,只寫[f, g],它仍然會導致錯誤。原因是,在列表中不可能有不同類型的值。

有時,當您想要將不同類型的值存儲在同一個列表中時,使用存在類型(GHC擴展)是有意義的。有了這個,你可以定義一個存在類型Comparator並使用列表[Comparator length, Comparator head]。然而,這與使用comparing沒有任何好處,就像您在第一個示例中那樣使用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,它是不可能的使用不根據密鑰進行比較的比較函數。正如我所說,它並沒有真正爲您的第一個版本增加任何好處。

+0

具有存在類型的解決方案將如何? – Alexandros 2013-02-13 08:55:34

+0

@Alexandros我給我的答案添加了一個例子。 – sepp2k 2013-02-13 12:46:24

+0

'[length,head] :: [[Int] - > Int]'。它不會是多態的(所以它不適用於OP的目的)。 – 2013-02-13 16:09:02