2017-01-10 27 views
2

如何根據自己的名字對動物進行排序?名稱由String值表示。如何按字符串排序自定義類型

data Animal = Cat String | Dog String | Fox String deriving (Show) 

testAnimals = [(Dog "c"),(Fox "a"),(Cat "b")] 

sortAnimalsByName :: [Animal] -> [Animal] 
sortAnimalsByName animals = undefined 

查詢:

sortAnimalsByName testAnimals 

應該返回:

[(Fox "a"),(Cat "b"),(Dog "c")] 

我認爲sort :: Ord a => [a] -> [a]Data.List應使用功能,但如何?

回答

8

首先你更好地定義一個animalName功能:

animalName :: Animal -> String 
animalName (Cat n) = n 
animalName (Dog n) = n 
animalName (Fox n) = n 

只需使用sortBy,其中比較簡直是compareonanimalName

import Data.Function(on) 
import Data.List(sortBy) 

sortAnimalsByName :: [Animal] -> [Animal] 
sortAnimalsByName = sortBy (compare `on` animalName) 

或者你可以 - 像@JonPurdy表明,使用comparing這與on compare基本相同:

import Data.Ord(comparing) 

sortAnimalsByName :: [Animal] -> [Animal] 
sortAnimalsByName = sortBy (comparing animalName) 

甚至更​​短:

import Data.List(sortOn) 

sortAnimalsByName :: [Animal] -> [Animal] 
sortAnimalsByName = sortOn animalName 

如果使用record-syntax可以進一步省略animalName定義:

--omitting the `animalName` function 
data Animal = Cat { animalName :: String} | 
       Dog { animalName :: String} | 
       Fox { animalName :: String} deriving (Show) 
+0

''(比較'f')'''Data.Ord.comparing f'; 'sortBy(比較f)'='Data.List.sortOn f' –

+0

@JonPurdy:我已經用你的建議更新了答案,非常感謝。 –

3

編輯謝謝@WillemVanOnsem的評論。

首先這應該是一個適當的總和類型,以便您可以避免重複字符串全部。意思是,你應該讓

type Animal a = Dog a | Cat a | Fox a

這種方式,你將有一個更一般的動物類型(這是一件好事)。更一般類型的優點之一是它可以讓你創建更精確的動物類型類實例。

不過你的情況最簡單的方法是將獲得ShowEq,然後寫一個奧德情況下,像這樣:

data Animal = Cat String | Dog String | Fox String deriving (Show, Eq) 

instance Ord Animal where 
    (Cat x) `compare` (Cat y) = x `compare` y 
    (Cat x) `compare` (Dog y) = x `compare` y 
    (Cat x) `compare` (Fox y) = x `compare` y 
    (Dog x) `compare` (Cat y) = x `compare` y 
    (Dog x) `compare` (Dog y) = x `compare` y 
    (Dog x) `compare` (Fox y) = x `compare` y 
    (Fox x) `compare` (Cat y) = x `compare` y 
    (Fox x) `compare` (Dog y) = x `compare` y 
    (Fox x) `compare` (Fox y) = x `compare` y 

data Animal a = Cat a | Dog a | Fox a deriving (Show, Eq) 

instance Ord a => Ord (Animal a) where 
    (Cat x) `compare` (Cat y) = x `compare` y 
    (Cat x) `compare` (Dog y) = x `compare` y 
    (Cat x) `compare` (Fox y) = x `compare` y 
    (Dog x) `compare` (Cat y) = x `compare` y 
    (Dog x) `compare` (Dog y) = x `compare` y 
    (Dog x) `compare` (Fox y) = x `compare` y 
    (Fox x) `compare` (Cat y) = x `compare` y 
    (Fox x) `compare` (Dog y) = x `compare` y 
    (Fox x) `compare` (Fox y) = x `compare` y 

您可以使用sort因爲現在AnimalOrd實例

-- sort :: Ord a => [a] -> [a] 

sort testAnimals 

要點是:使用類型類。這只是簡單類型的最簡單的方法。

+2

這不會根據動物的*名稱*對對象進行排序。無論名稱如何,默認的'Ord'總是會將低於'Dog'和'Dog'的Cat放在低於'Fox'的地方(或者在你的情況下'Dog' <'Cat' <'Fox')。如果是平局,名稱確實會被使用。但'貓'zz「'會比'Fox'aa''少。它首先根據它的參數對構造函數('Cat','Dog','Fox')然後是 - iirc進行排序。 –

+0

@WillemVanOnsem謝謝。我只是編輯了答案。 – urbanslug

0

加入的答案,通過@Willem範Onsem(記錄語法)和@urbanslug給出(實施Ord類的實例),我們得到的最簡單的一個:

data Animal a = Cat {name :: a} 
       | Dog {name :: a} 
       | Fox {name :: a} deriving (Show, Eq) 

instance Ord a => Ord (Animal a) where 
    a1 `compare` a2 = name a1 `compare` name a2 

因此,它可以簡單地說

λ> sort [(Dog "c"),(Fox "a"),(Cat "b")] 
[Fox {name = "a"},Cat {name = "b"},Dog {name = "c"}] 

使用Data.Ord.comparing功能一個coud寫更精確:

instance Ord a => Ord (Animal a) where compare = comparing name