如何编写比较器链接功能?

时间:2013-02-13 00:08:13

标签: haskell ghc

我正在尝试编写一个接受比较器列表并返回比较器的函数,该比较器将使用第一个比较器比较一对值,如果第一个比较器返回EQ等则返回第二个比较器。 / p>

我想出的是以下功能:

import Data.Monoid

chainCompare :: [a -> a -> Ordering] -> a -> a -> Ordering
chainCompare = mconcat . map ($)

编辑:chainCompare也可以写成(感谢Vitus指出):

chaincompare = mconcat

使用此功能的示例如下:

import Data.List
import Data.Ord

sortBy (chainCompare [comparing length, comparing sum]) [[1..100], [1..20], [100..200]]

但是,此功能需要明确使用比较,所以我尝试修改这个函数:

chainCompare :: Ord b => [a -> b] -> a -> a -> Ordering
chainCompare = mconcat . map (comparing $)

但是,chainCompare在这种情况下会导致编译错误(另外,即使这个例子编译了,它也不适用于空字符串):

 sortBy (chainCompare [length, head]) [['a'..'z'], ['A'..'Z'], "Lorem ipsum dolor sit amet"]

chainCompare可以是任何类型的b,是否可以使instance Ord多态?我已经看到了一些使用forall扩展的Haskell代码,并尝试搜索它们,但我仍然无法弄清楚每个特定扩展名的用途。

1 个答案:

答案 0 :(得分:6)

如果chainCompare [f, g]f是具有不同类型的函数,

g将始终导致错误 - 无论您如何定义chainCompare。您甚至可以删除chainCompare并只写[f, g],但仍会导致错误。原因在于,根本不可能在列表中包含不同类型的值。

有时,当您想要将不同类型的值存储在同一列表中时,使用存在类型(GHC扩展)是有意义的。有了它,您可以定义存在类型Comparator并使用列表[Comparator length, Comparator head]。但是,这比使用comparing没有任何好处,就像你在第一个例子中所做的那样,所以在这种情况下它将毫无意义。

因此,使用comparing的第一个代码确实是您能做的最好的代码。

对于记录,这里是代码使用存在类型的样子:

{-# LANGUAGE ExistentialQuantification #-}

import Data.Monoid
import Data.Ord

data Comparator a = forall b. Ord b => Comparator (a -> b)

chainCompare :: [Comparator a] -> a -> a -> Ordering
chainCompare = mconcat . map comp
  where comp (Comparator f) = comparing f

-- Usage:
list = [['a'..'z'], ['A'..'Z'], "Lorem ipsum dolor sit amet"]
sortedList = sortBy (chainCompare [Comparator length, Comparator head]) list

与第一个版本相比,唯一的区别在于您必须编写Comparator而不是comparing,并且无法使用不基于密钥进行比较的比较函数。正如我所说,它并没有为你的第一个版本增加任何好处。