【问题标题】:Creating a new Ord instance for Lists为 Lists 创建一个新的 Ord 实例
【发布时间】:2012-05-31 16:31:41
【问题描述】:

这是我第一次尝试创建 Ord 等类的自定义实例。

我已经定义了一个新的数据结构来表示一个列表:

data List a = Empty | Cons a (List a)
    deriving (Show, Eq)

现在我想为 List 定义一个新的 Ord 实例,使得 List a

首先,是否有必要定义一个新的“sum”函数,因为 Prelude 中定义的 sum 不适用于新的 List 数据类型?那么,如何为 List 定义新的 Ord 实例呢?

谢谢

【问题讨论】:

    标签: haskell typeclass instances


    【解决方案1】:

    首先,它不会像普通的列表实例那样工作。普通实例仅取决于列表中的项目本身是可订购的;您的提案取决于它们的数字(例如在Num 类中),因此更窄。

    需要定义一个新的sum函数。令人高兴的是,将sum 编写为一个简单的递归函数非常容易。 (巧合的是,你可以调用你的函数sum',发音为“sum prime”,按照惯例,它是一个与sum非常相似的函数。)

    此外,实例必须依赖于Num 类以及Ord 类。

    一旦你有了一个新的sum 函数,你就可以像这样定义一个实例:

    instance (Ord n, Num n) => Ord (List n) where compare = ... 
      -- The definition uses sum'
    

    这个实例语句可以理解为对于所有类型n,如果nOrdNum 中,List nOrd 中,比较工作如下。语法与 => 是隐含的数学非常相似。希望这样可以更容易记住语法。

    你必须给compare一个合理的定义。作为参考,compare a b 的工作方式如下:如果a < b 它返回LT,如果a = b 它返回EQ,如果a > b 它返回GT

    这是一个易于实现的函数,因此我将把它作为练习留给读者。 (我一直想说:P)。

    【讨论】:

    • 优秀的答案,如果可以的话,我会投赞成票...一旦你面前有答案,Haskell 就会非常直观
    【解决方案2】:

    怎么样...

    newtype List a = List [a]
    

    如果你想为给定类型引入新的、“不兼容”的类型类实例,这很常见(参见例如ZipList 或几个像 SumProduct 这样的幺半群)

    现在您可以轻松地重用列表的实例,并且您也可以使用sum

    【讨论】:

      【解决方案3】:

      稍微概括一下@Tikhon 的方法,您也可以使用Monoid 而不是Num 作为约束,其中您已经有一个与mconcat 的预定义“总和”(当然,您仍然需要Ord)。这将为您提供更多类型,而不仅仅是数字(例如List (List a),您现在可以轻松地递归定义)

      另一方面,如果你确实想使用Num 作为幺半群,你必须每次都决定是Sum 还是Product。有人可能会说,必须明确地写出来会降低简短性和可读性,但这是一种设计选择,取决于您最终希望拥有何种程度的通用性。

      【讨论】:

      【解决方案4】:

      怎么样..

      data List a = Empty | Cons a (List a)
                      deriving (Show, Eq)
      
      
      instance (Ord a, Num a, Eq a) => Ord (List a) where
      
            -- 2 empty lists
      
            Empty <= Empty            =   True
      
            -- 1 empty list and 1 non-empty list
      
            Cons a b <= Empty         =   False
            Empty <= Cons a b         =   True
      
            -- 2 non-empty lists
      
            Cons a b <= Cons c d      =   sumList (Cons a b) <= sumList (Cons c d) 
      
      
      -- sum all numbers in list
      
      sumList         ::      (Num a) => List a -> a
      
      sumList Empty               =       0
      sumList (Cons n rest)       =       n + sumList rest
      

      这是你要找的吗?

      【讨论】:

        【解决方案5】:

        .. 或 Prelude 中使用 sum 函数的其他解决方案。

        data List a = Empty | Cons a (List a)
                        deriving (Show, Eq)
        
        
        instance (Ord a, Num a, Eq a) => Ord (List a) where
        
              -- 2 empty lists
        
              Empty <= Empty            =   True
        
              -- 1 empty list and 1 non-empty list
        
              Cons a b <= Empty         =   False
              Empty <= Cons a b         =   True
        
              -- 2 non-empty lists
        
              Cons a b <= Cons c d      =   sum (listToList (Cons a b)) 
                                                      <= sum (listToList (Cons c d)) 
        
        
        -- convert new List to old one
        
        listToList      ::      (Num a) => List a -> [a]
        
        listToList Empty                =       []
        listToList (Cons a rest)        =       [a] ++ listToList rest
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-20
          • 1970-01-01
          • 2012-06-22
          • 1970-01-01
          • 2023-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多