我正在浏览Data.List中的nubBy
函数的源代码:
nubBy :: (a -> a -> Bool) -> [a] -> [a]
#ifdef USE_REPORT_PRELUDE
nubBy eq [] = []
nubBy eq (x:xs) = x : nubBy eq (filter (\ y -> not (eq x y)) xs)
#else
nubBy eq l = nubBy' l []
where
nubBy' [] _ = []
nubBy' (y:ys) xs
| elem_by eq y xs = nubBy' ys xs
| otherwise = y : nubBy' ys (y:xs)
现在在我看来,上面的两个版本彼此不同。
如果我使用USE_REPORT_PRELUDE
版本,我会
nubBy (>) [1..10]
[1,2,3,4,5,6,7,8,9,10]
而另一个实现产生
nubBy (>) [1..10]
[1]
这背后的原因是什么?
答案 0 :(得分:14)
我认为nubBy
要求二进制布尔运算是等价关系。
这与sortBy
的精神大致相同,需要预先排序关系(反身和传递)。如果此要求无效,则quicksort和mergesort将成为非等效算法。 Haskell报告的目的是允许实现使用它们中的任何一个(或其他正确的排序算法)。
类似地,如果允许nubBy
比较是非等价的,则实施被不必要地限制为精确使用引用Prelude
算法,从而阻止使用更有效的替代方法。
老实说,对运营商的确切要求传递给了" ... By"并不总是显而易见的。 例如,sortBy的文档似乎只保证对总排序的正确性,虽然我期望实际的实现也适用于更大类的排序,只要结果被解释为由排序引起的等价。
nubBy的文档只是声明第一个参数是"用户提供的等式谓词"。因此,它只能保证平等,而不是任意等价。
但是,我的感觉是,如果它的实现适用于相等,那么 也可以用于等价(当然,只要读取结果)。这可能通过利用" free theorem"与nubBy
类型相关联。我缺乏参数化方面的专业知识背叛了我:)
答案 1 :(得分:3)
有a GHC bug report这个。 nubBy
的行为最初与Prelude实施相匹配,但在某些时候被更改为another bug report的“修复”。我认为陪审团仍然没有做正确的事情。
您可以在codepad.org上看到,您的代码确实产生了[1,2,3,4,5,6,7,8,9,10]
;但是在ideone.com上,您的代码会生成[1]
。很明显,人们使用较旧的或不同的实现。