Haskell 对列表使用不同的排序

Haskell 对列表使用不同的排序,haskell,Haskell,在Haskell中,[a]的默认排序(给定a上的排序)似乎是一种词典排序(附带问题:我在哪里可以找到这种情况)?我想要的是分级词典排序(也称为“长度加词典”排序) 我将如何指定我希望以分级词典的方式进行比较?我只想要一种,不是所有的。我试过这个: instance Ord [Int] where compare xs ys = case compare (length xs) (length ys) of LT -> LT

在Haskell中,[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.

谢谢你的帮助

这是
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)
newtype GradedLexOrd a=GradedLexOrd{runGradedLexOrd::[a]}
实例(Ord a)=>Ord(GradedLexOrd a),其中
比较(GradedLexOrd xs)(GradedLexOrd ys)=GradedLexOrd xs ys
gradedLexOrd::Ord a=>[a]->[a]->排序
gradedLexOrd=比较长度比较——很好的基于幺半群的实现,
--由于Aaron Roth(参见下面的答案)

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

Ord[a]
看起来怎么样? 当然,您可以在GHCi中进行试验,但也许您需要更可靠的产品。这是非常困难的,尤其是列表的定义(由于其特殊的语法)内置在编译器中。让我们问问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
中定义的,在那里它说:

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]
有一个实例,但只能有一个。使用
FlexibleInstances
OverlappingInstances
,您可以让它使用另一个实例,比如说,
[Int]
,但它的样式不好。正如leftaroundabout所写,使用一个
NewtypeWrapper
,或者使用参数化函数,比如
sortBy
,为
Int
列表创建一个全新的
Ord
实例对我来说似乎有点重(更不用说,您可能在散布混乱:稍后访问您的代码的人可能会预期默认的、未分级的词典比较行为)

如果您只是希望不必在每次使用
或类似的方法时复制自定义比较代码,那么实际上有一种相当轻量级的方法可以当场定义类似于您的链接比较函数。
排序
,实际上是
Monoid
的一个实例,这意味着您可以比较两件事根据一系列标准,然后使用
Monoid
函数,
mappend
(最近缩写为
)将这些比较结果的
排序
组合在一起。这些都在中有详细解释,这也是我学会的窍门。因此:

import Data.Monoid ((<>))
import Data.Ord (comparing)

gradedLexicographicCompare :: (Ord a) => [a] -> [a] -> Ordering
gradedLexicographicCompare xs ys = comparing length xs ys <> comparing id xs ys
这还有一个优点,您的继任者会立即看到您正在使用自定义比较函数

更新:leftaroundabout在下面指出,我们可以实现更高的优雅度——这毕竟是Haskell,在Haskell中,我们总是可以通过使用monoid实例,
实例monoid b=>monoid(a->b)来实现更高的优雅度
。也就是说,结果为幺半群的函数本身可以被视为幺半群

instance Monoid b => Monoid (a -> b) where
  mempty _ = mempty
  mappend f g x = f x `mappend` g x   (1)
现在让我们沉迷于一点等式推理,看看
比较长度比较
根据这个实例扩展到什么

comparing length <> compare
    = mappend (comparing length) compare
    = \xs -> mappend ((comparing length) xs) (compare xs)   (2)
但是现在
((比较长度)xs)ys)
((比较长度)ys)
是完全应用的函数。具体来说,它们是
排序
s,从最初的答案中,我们知道如何使用
mappend
Monoid
排序实例中组合两个
排序
(注意,我们没有使用(1)中的
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 <>]

漂亮。

您是否尝试过将
-XFlexibleInstances
添加到GHC参数中,正如错误所示?@RobinGreen:这不会有帮助,因为显然已经存在冲突的
Ord[Int]
instance,诽谤性词典编纂的一个。罗宾·格林,谢谢你的建议。我还没有尝试过,我该如何添加这个选项?@Calle:要打开这样的扩展,请将
{-#LANGUAGE FlexibleInstances}
放在源文件的顶部。但同样,这对你的情况没有帮助。Haskell报告:“可以为构成类型在
Ord
中的任何用户定义的数据类型派生
Ord
的实例。数据声明中构造函数的声明顺序决定派生
Ord
实例中的顺序。”和“
data[a]=[]a:[a]派生(Eq,Ord)
“合在一起意味着是的,它是词典。谢谢,这正是我需要的。+1因为使用了好的幺半群东西——巧合的是,我最近遇到了排序的幺半群实例,想知道这有什么用——现在我知道了。这真的很好,因为还有
实例幺半群b=>幺半群(a->b)
。因此实际上,您可以简单地编写
排序(比较长度比较)
:每个比较的两个参数都由函数monoid实例自动“拉入”。
mappend ((comparing length) xs) (compare xs)
    = \ys -> mappend (((comparing length) xs) ys) ((compare xs) ys)   (3)
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 = comparing length <> compare