2

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

我想出的是以下功能:

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的意义上进行多态?我已经看到一些使用扩展的 Haskell 代码并尝试搜索它们,但我仍然无法弄清楚每个特定扩展的用途。binstance Ordforall

4

1 回答 1

7

chainCompare [f, g]f如果和g是具有不同类型的函数,无论您如何定义,总是会导致错误chainCompare。您甚至可以删除chainCompare并直接写入[f, g],它仍然会导致错误。原因是列表中根本不可能有不同类型的值。

有时,当您想在同一个列表中存储不同类型的值时,使用存在类型(GHC 扩展)是有意义的。有了它,您可以定义一个存在类型Comparator并使用 list [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并且不可能使用不基于键进行比较的比较函数。因此,正如我所说,与您的第一个版本相比,它并没有真正增加任何好处。

于 2013-02-13T00:20:11.613 回答