我正在尝试编写一个接受比较器列表并返回比较器的函数,该比较器将使用第一个比较器比较一对值,如果第一个比较器返回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代码,并尝试搜索它们,但我仍然无法弄清楚每个特定扩展名的用途。
答案 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
,并且无法使用不基于密钥进行比较的比较函数。正如我所说,它并没有为你的第一个版本增加任何好处。