【问题标题】:Is it possible to implement a general in-place quicksort in haskell?是否可以在 haskell 中实现通用的就地快速排序?
【发布时间】:2013-02-03 18:19:44
【问题描述】:

问题中的术语general(与specialized相反)意味着该函数可以对项目进行排序,只要它们属于@987654326实例的类型@.

考虑一下最著名的 Haskell 广告之一

quicksort :: Ord a => [a] -> [a]
quicksort []     = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
    where
        lesser  = filter (< p) xs
        greater = filter (>= p) xs

上面的实现没有到位。
我试图写一个就地版本。 就地进行快速排序很容易。通常,我们只需要一个可变数组,我选择了Foreign.Marshal.Array
我的实现是就地的并且运行得很好,但是我对它的类型签名不满意

(Ord a, Storable a) => [a] -> IO [a]

更准确地说,类型约束Storable a 惹恼了我。

显然,如果我们要对项目进行排序,则需要 Ord 约束,而 Storable 是不必要的。
相比之下,经典快速排序或sort 中的Data.List 的类型签名是Ord a =&gt; [a] -&gt; [a]。约束只是Ord

我没有找到摆脱额外约束的方法。

我搜索了 Stackoverflow,发现了一些关于 Haskell 中就地快速排序的问题,例如
How do you do an in-place quicksort in Haskell
Why is the minimalist, example Haskell quicksort not a "true" quicksort?

不幸的是,他们的主要关注点就位。那里给出的所有就地快速排序示例也有额外的类型约束。
例如,iqsort given by klapaucius 具有类型签名

iqsort :: (Vector v a, Ord a) => v a -> v a

有人知道如何使用类型签名Ord a =&gt; [a] -&gt; [a] 实现就地快速排序haskell 函数吗?
我知道如何进行就地快速排序,但我不知道如何使其通用

【问题讨论】:

  • 不是 Haskell 的意图(我的意思是 pure 函数式语言)针对就地类型的事情(因为就地修改是一种侧面-函数的效果,如果修改参数就不是纯函数)
  • 就地和Ord a =&gt; [a] -&gt; [a] 在一起没有意义。 Haskell 列表根本不会就地执行。您将需要使用 IO 或 ST 或 State 进行就地,因为就地意味着可变性。请注意,Vector v a 的实例充满了易于拆箱的固定大小类型。
  • @VB9-UANIC 如果用户无法检测到突变,可以将突变隐藏在纯界面下。例如,请参阅 ST monad。
  • 为什么是ForeignIOArraySTArray 有什么问题?
  • 作为其他人谈论的示例,请参阅我在this previous answer 中的vsort 函数。

标签: haskell quicksort


【解决方案1】:

iqsort 在我看来实际上完全一般。如果您查看 Data.Vector.Generic haddocks,您实际上可以将该接口用于 any a!不同之处在于给定的函数是 more 通用的,因为它允许您选择一个 unboxed 向量,这当然只适用于 some a.

这是链接:http://hackage.haskell.org/packages/archive/vector/0.10.0.1/doc/html/Data-Vector-Generic.html

因此,如果您选择要装箱的 V,矢量约束就会消失。

【讨论】:

  • 换一种说法:Ord a 约束是泛化到“任意”a 所必需的,而Vector v a 约束是泛化到“任意”向量所必需的。通过专门针对特定的向量类型,您消除了Vector v a 约束,正如通过专门针对特定的a 类型,您消除了Ord a 约束。
  • @DanBurton 我的理解是:Vector v a 是对a 的限制。 [a] 也是对 'a' 的约束,并且比 Vector v a 更强。即,[a]下的所有a都可以看作是一些Vector v aVector [] a 尚未实现,但添加起来很简单。对吗?
  • 这毫无意义。 Vector v a 是一个类型类约束。 [a]a 中的多态类型,而不是约束。您不想就地对 list 进行排序。 [a] 是 a 的列表,它是一个单链表。您想使用MVector ST a,它是 ST monad 中a 的可变向量。
  • 我知道我的错误在哪里。带有类型签名Ord a =&gt; [a] -&gt; [a]quicksortquicksort 不同,只要它们属于Ord 的实例类型,它们就可以对项目进行排序。实际上,klapaucius 给出的iqsort 是一个有效的通用就地快速排序。
【解决方案2】:

是的,这是可能的。 (尽管在 Haskell 中,您只想在真正需要最高性能的情况下使用这种命令式算法。)

我知道 2 种这样的算法:

  • sort 来自 矢量算法
  • qsort(或introsort)来自marray-sort,我还没有发布到Hackage。 (如果需要,请告诉我。)它适用于 mutable arrays

Introsort 基本上是精炼的快速排序,具有 O(n log n) 最坏情况复杂度。)

我不确定MVector,但对于MArrays,您不必担心MArray a e m 的额外约束。他们在那里使类型更通用,而不是更少。像

这样的签名
qsort :: (MArray a e m, Ord e) => a Int e -> m ()

允许对不同的数组表示使用相同的算法。对于某些数据类型,您可以拥有该类型的专用数组,它们比通用数组更快、更紧凑。例如,如果要对 8 位整数进行排序,则有一个专门的实例 MArray IOUArray Int8 IO 用于 unboxed arrays。对于这种仅使用多态性的数组,qsort 的专门化是

qsort :: IOUArray Int Int8 -> IO ()

但您也有实例 MArray IOArray e IO 可以任意工作 e。通过将qsortIOArray 一起使用,您可以获得对e 没有限制的专业化:

qsort :: (Ord e) => IOArray Int e -> IO ()

此外,如果您使用 STArrays 和 ST monad,您可以使用相同的函数对数组进行就地排序,稍后将结果作为纯值获取,而无需 IO

【讨论】:

  • 啊! MArray IOArray e IO,我怎么能错过呢?我以为他们都是专业的。非常感谢!
猜你喜欢
  • 2013-11-14
  • 2012-02-24
  • 2017-09-04
  • 1970-01-01
  • 2011-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多