【问题标题】:How to sort custom Type by String如何按字符串对自定义类型进行排序
【发布时间】:2017-01-10 11:46:46
【问题描述】:

如何按动物名称对动物进行分类?名称由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")]

我认为应该使用来自Data.List 的函数sort :: Ord a => [a] -> [a],但是如何使用呢?

【问题讨论】:

    标签: list sorting haskell


    【解决方案1】:

    首先你最好定义一个animalName函数:

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

    只需使用sortBy,其中比较器只是compare,但on animalName

    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)
    

    【讨论】:

    • (compare `on` f) = Data.Ord.comparing f; sortBy (comparing f) = Data.List.sortOn f
    • @JonPurdy:我已经根据您的建议更新了答案,非常感谢。
    【解决方案2】:

    已编辑感谢@WillemVanOnsem 的评论。

    首先这应该是一个正确的 sum 类型,这样你就可以避免重复 String。意思是,你应该成功

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

    这样您将拥有更通用的 Animal 类型(这是一件好事)。更通用类型的优点之一是它允许您创建更精确的 Animal 类型类实例。

    但是,在您的情况下,最简单的方法是派生 ShowEq,然后像这样编写 Ord 实例:

    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,因为现在Animal 有一个Ord 实例

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

    要点是:使用类型类。这只是简单类型的最简单方法。

    【讨论】:

    • 这不会根据动物的名称对对象进行排序。默认的Ord 将始终对低于DogCat 和低于FoxDog 进行排序(或者在您的情况下为Dog Cat Fox),无论它们的名称如何。在平局的情况下,确实会使用该名称。但是Cat "zz" 会小于Fox "aa"。它首先对构造函数(CatDogFox)进行排序,然后 - iirc - 对其参数进行排序。
    • @WillemVanOnsem 谢谢。我刚刚编辑了答案。
    【解决方案3】:

    结合@Willem Van 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 函数可以写得更准确:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-15
      • 2014-02-08
      • 2014-07-21
      • 2012-06-02
      • 1970-01-01
      • 2021-07-29
      • 2011-01-13
      • 2020-05-29
      相关资源
      最近更新 更多