为什么不排序(a - > a - > Bool)?

时间:2012-08-28 03:57:15

标签: sorting haskell

Haskell sortBy函数将(a -> a -> Ordering)作为其第一个参数。任何人都可以教育我有什么推理吗?我的背景完全采用具有类似功能的语言而不是(a -> a -> Bool),因此必须编写一个返回LT / GT的语言有点令人困惑。

这是使用静态类型/纯函数语言执行此操作的标准方法吗?这是ML下降语言特有的吗?是否有一些我没有看到的基本优势,或者使用布尔值隐藏 dis 优势?


汇总:

  • Ordering不是GT | LT,实际上是GT | EQ | LT(显然GHC为了排序而没有利用这个内容,但仍然)

  • 更接近地回归三分法值来模拟两个元素比较的可能结果

  • 在某些情况下,使用Ordering而不是Bool会保存比较

  • 使用Ordering可以更轻松地实施稳定排序

  • 使用Ordering向读者清楚地表明正在进行两个元素之间的比较(布尔值本身并不具备这个意义,尽管我感觉很多读者会认为它)< / p>

我暂时接受了Carl的回答,并发布了上述摘要,因为截至此编辑时,没有任何一个回答达到所有要点。

5 个答案:

答案 0 :(得分:23)

我认为Boolean Blindness是主要原因。 Bool是一种没有域语义的类型。在像sortBy这样的函数的情况下,它的语义完全来自约定,而不是来自函数运行的域。

这为编写比较函数所涉及的心理过程增加了一个间接层。而不只是说“我可以返回的三个值小于,等于或大于”,排序的语义构建块,你说“我想要返回少于,所以我必须将它转换为布尔值”。还有一个额外的心理转换步骤始终存在。即使你精通大会,它仍会让你慢下来。如果你不熟悉惯例,你必须检查看它是什么,你会放慢脚步。

它是3值而不是2值这一事实意味着您不需要在排序实现中非常小心地获得稳定性 - 但这是一个次要的实现细节。这并不像实际让你的价值观具有意义那么重要。 (另外,Bool并不比Ordering更有效。它不是Haskell中的原语。它们都是库中定义的代数数据类型。)

答案 1 :(得分:20)

当你整理东西时,你把它们整理好;没有确定的“真实”价值。

更重要的是,“真实”意味着什么?第一个参数小于第二个?比...更棒?现在你要覆盖“真实”,真正意味着“小于”(或“大于”,取决于你选择如何实现这个功能)。如果他们是平等的呢?

那么,为什么不切断中间人,可以这么说,并返回你真正的意思?

答案 2 :(得分:11)

没有理由不能。如果您查看ghc implementation,它只会检查结果是否为GT。代码的Haskell报告版本使用insertBy,它同样仅检查GT。您可以编写以下内容并毫无问题地使用它:

sortByBool :: (a -> a -> Bool) -> [a] -> [a]
sortByBool lte = sortBy (\x y -> if lte x y then LT else GT)

sort' :: Ord a => [a] -> [a]
sort' = sortByBool (<=)

通过了解元素何时EQ,可以想象某些种类可以执行优化,但当前使用的实现不需要此信息。

答案 3 :(得分:6)

我认为有两个单独的设计决定:
1)创建Ordering类型
2)选择sortBy返回Ordering

Ordering类型不仅仅适用于sortBy - 例如,compareOrd类型类的“核心”。它的类型是:: Ord a => a -> a -> Ordering。给定两个值,然后,您可以发现它们是否小于,大于或等于任何其他比较函数((<)(<=)(>),{{ 1}}),你只能排除这三种可能性中的一种。

这是一个简单的例子,其中(>=)(至少在我看来)使函数的意图更清晰一点:

Ordering

一旦你决定创建f a b = case compare a b of GT -> {- something -} LT -> {- something -} EQ -> {- something -} 类型,那么我认为在那些你正在寻找的信息(如Ordering)的地方使用它是很自然的,而不是使用sortBy作为一种解决方法。

答案 4 :(得分:6)

在我们确实需要区分Ordering案例的情况下,保存比较需要三个值EQ。在重复保留sortmerge时,我们会忽略EQ个案,因此 谓词 less-then - 或等于语义是完全可以接受的。但不是unionnubSort我们确实希望区分三个比较结果。

mergeBy lte (x:xs) (y:ys)
    | lte y x   = y : mergeBy lte (x:xs) ys
    | otherwise = x : mergeBy lte xs (y:ys)

union (x:xs) (y:ys) = case compare x y of 
    LT -> x : union  xs (y:ys) 
    EQ -> x : union  xs    ys 
    GT -> y : union (x:xs) ys

lte 谓词编写后者是不自然的。