在列表上使用不同的排序

时间:2013-11-10 12:30:40

标签: haskell

在Haskell中,[a]的默认排序,给定a的排序,似乎是一个词典排序(侧面问题:我在哪里可以找出是否真的如此)?我想要的是一个分级的词典排序(也称为“长度加词典”排序)。

我如何指定我希望以分级的词典编纂方式进行比较?我只想要一种类型,而不是所有[a]。我试过这个:

instance Ord [Int] where
  compare xs ys = case compare (length xs) (length ys) of
                          LT -> LT
                          GT -> GT
                          EQ -> lexicographic_compare xs ys

但收到此错误消息:

> [1 of 1] Compiling Main             ( test.hs, interpreted )
test.hs:1:10:
    Illegal instance declaration for `Ord [Int]'
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use -XFlexibleInstances if you want to disable this.)
    In the instance declaration for `Ord [Int]'
Failed, modules loaded: none.

感谢您的帮助!

3 个答案:

答案 0 :(得分:10)

这是newtype包装器的典型应用程序:

newtype GradedLexOrd a = GradedLexOrd { runGradedLexOrd :: [a] }

instance (Ord a) => Ord (GradedLexOrd a) where
  compare (GradedLexOrd xs) (GradedLexOrd ys) = gradedLexOrd xs ys

gradedLexOrd :: Ord a => [a] -> [a] -> Ordering
gradedLexOrd = comparing length <> compare -- Nice Monoid-based implementation,
                                           --due to Aaron Roth (see answer below)

或者,您可以公开使用列表,但不是像Ord这样的sort约束函数,而是使用接受自定义比较函数的更通用的替代函数,例如: sortBy gradedLexOrd

答案 1 :(得分:6)

这里有两个问题:

Ord [a]的外观如何?

当然你可以在GHCi内进行试验,但也许你想要更可靠的东西。这是非常困难的,特别是因为Lists的定义(由于它们的特殊语法)内置于编译器中。我们问GHCi:

Prelude> :info []
data [] a = [] | a : [a]    -- Defined in `GHC.Types'
instance Eq a => Eq [a] -- Defined in `GHC.Classes'
instance Monad [] -- Defined in `GHC.Base'
instance Functor [] -- Defined in `GHC.Base'
instance Ord a => Ord [a] -- Defined in `GHC.Classes'
instance Read a => Read [a] -- Defined in `GHC.Read'
instance Show a => Show [a] -- Defined in `GHC.Show'

它表示该实例是在GHC.Classes中定义的,we find in GHC’s git repo,并且在那里说:

instance (Ord a) => Ord [a] where
        {-# SPECIALISE instance Ord [Char] #-}
        compare []     []     = EQ
        compare []     (_:_)  = LT
        compare (_:_)  []     = GT
        compare (x:xs) (y:ys) = case compare x y of
                                    EQ    -> compare xs ys
                                    other -> other

是的,确实是词典排序。

如何覆盖订购?

别。 [a]有一个实例,只有一个实例。使用FlexibleInstancesOverlappingInstances,您可以让它使用替代实例,例如[Int],但这样做很糟糕。如左下方所述,请使用NewtypeWrapper,或使用sortBy等参数化函数。

答案 2 :(得分:5)

Ord列表创建一个全新的Int实例似乎对我的品味有点重要(更不用说你可能会播下混乱:以后可能会遇到你的代码的人期望默认的,非分级的词典比较行为)。

如果您只是希望每次使用sortBy等时都不必复制自定义比较代码,那么实际上有一种相当轻量级的方法来定义像您这样的链式比较函数。 Ordering实际上是Monoid的一个实例,这意味着您可以根据一系列标准比较两件事,然后使用{组合这些比较的结果Ordering。 {1}}函数,Monoid(最近缩写为mappend)。在Learn You a Haskell chapter on Monoids, etc.中对此进行了详细解释,这是我接受技巧的地方。所以:

<>

(当然,import Data.Monoid ((<>)) import Data.Ord (comparing) gradedLexicographicCompare :: (Ord a) => [a] -> [a] -> Ordering gradedLexicographicCompare xs ys = comparing length xs ys <> comparing id xs ys 只是comparing id,但为了统一......)然后编写像

这样的东西变得相对繁琐
compare

此外,您的继任者将立即看到您正在使用自定义比较功能。

更新:leftaroundabout指出我们可以实现更高的优雅 - 毕竟这是Haskell,而在Haskell中我们总能实现更高的优雅 - 通过利用monoid实例, f = ... sortBy s ... where ... s xs ys = comparing length xs ys <> compare xs ys ... 。也就是说,其结果是幺半群的函数本身可以被认为是幺半群。该实例由

给出
instance Monoid b => Monoid (a -> b)

现在让我们沉迷于一个小的等式推理,看看instance Monoid b => Monoid (a -> b) where mempty _ = mempty mappend f g x = f x `mappend` g x (1) 根据这个实例扩展到什么。应用(1)一次,我们有

comparing length <> compare

comparing length <> compare = mappend (comparing length) compare = \xs -> mappend ((comparing length) xs) (compare xs) (2) ((comparing length) xs) :: [a] -> Ordering本身就是其结果为幺半群的函数,即(compare xs) :: (Ord a) => a -> Ordering s,因此我们可以第二次应用(1)来获取

Ordering

但现在mappend ((comparing length) xs) (compare xs) = \ys -> mappend (((comparing length) xs) ys) ((compare xs) ys) (3) (((comparing length) xs) ys)是完全应用的功能。具体来说,它们是((compare xs) ys) s,根据原始答案,我们知道如何使用{strong> Ordering 实例中的Ordering来组合两个mappend Ordering。 (请注意,我们使用(1)中的Monoid。)在一个大链中写下所有内容,我们有

mappend

这次扩展的最后一行就是我们原来的comparing length <> compare = mappend (comparing length) compare [definition of <>] = \xs -> mappend ((comparing length) xs) (compare xs) [by (1)] = \xs -> (\ys -> mappend (((comparing length) xs) ys) ((compare xs) ys)) [substituting (3) in (2)] = \xs -> \ys -> mappend (comparing length xs ys) (compare xs ys) [function application is left associative] = \xs -> \ys -> comparing length xs ys <> compare xs ys [definition of <>] !经过长时间的长篇大论之后,我们可以写下优雅的无点点

gradedLexicographicCompare

相当