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

Ale*_*ros 1 haskell ghc

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

我想出的是以下功能:

import Data.Monoid

chainCompare :: [a -> a -> Ordering] -> a -> a -> Ordering
chainCompare = mconcat . map ($)
Run Code Online (Sandbox Code Playgroud)

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

chaincompare = mconcat
Run Code Online (Sandbox Code Playgroud)

使用此功能的示例如下:

import Data.List
import Data.Ord

sortBy (chainCompare [comparing length, comparing sum]) [[1..100], [1..20], [100..200]]
Run Code Online (Sandbox Code Playgroud)

但是,这个函数需要明确地使用比较,所以我试着像这样修改函数:

chainCompare :: Ord b => [a -> b] -> a -> a -> Ordering
chainCompare = mconcat . map (comparing $)
Run Code Online (Sandbox Code Playgroud)

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

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

是否可以在任何类型chainCompare的意义上使多态?我已经看到一些使用扩展的Haskell代码并尝试搜索它们,但我仍然无法弄清楚每个特定扩展对哪些有用.binstance Ordforall

sep*_*p2k 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
Run Code Online (Sandbox Code Playgroud)

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