好的,看看这个。
选择从可散列包中复制的部分,以及巫毒魔法语言编译指示。
{-# LANGUAGE FlexibleInstances, UndecidableInstances, NoMonomorphismRestriction, OverlappingInstances #-}
import System.Random (mkStdGen, next, split)
import Data.List (foldl')
import Data.Bits (shiftL, xor)
class Hashable a where
hash :: a -> Int
instance (Integral a) => Hashable a where
hash = fromIntegral
instance Hashable Char where
hash = fromEnum
instance (Hashable a) => Hashable [a] where
hash = foldl' combine 0 . map hash
-- ask the authors of the hashable package about this if interested
combine h1 h2 = (h1 + h1 `shiftL` 5) `xor` h2
好的,所以现在我们可以列出任何内容Hashable
并将其转换为Int
. 我在这里提供了Char
实例Integral a
,更多更好的实例在可散列包中,它也允许加盐和其他东西。
这一切只是为了我们可以制作一个数字生成器。
genFromHashable = mkStdGen . hash
所以现在有趣的部分。让我们编写一个函数,它接受一个随机数生成器、一个比较器函数和一个列表。然后我们将通过查询生成器来选择枢轴和比较器来划分列表来对列表进行排序。
qSortByGen _ _ [] = []
qSortByGen g f xs = qSortByGen g'' f l ++ mid ++ qSortByGen g''' f r
where (l, mid, r) = partition (`f` pivot) xs
pivot = xs !! (pivotLoc `mod` length xs)
(pivotLoc, g') = next g
(g'', g''') = split g'
partition f = foldl' step ([],[],[])
where step (l,mid,r) x = case f x of
LT -> (x:l,mid,r)
EQ -> (l,x:mid,r)
GT -> (l,mid,x:r)
库函数:从生成器中获取next
一个Int
,并生成一个新的生成器。split
将生成器分叉为两个不同的生成器。
我的功能:partition
用于f :: a -> Ordering
将列表划分为三个列表。如果你知道折叠,它应该很清楚。(请注意,它不会保留子列表中元素的初始顺序;它会反转它们。如果它是一个问题,使用 foldr 可以解决这个问题。)qSortByGen
就像我之前说的:咨询生成器以获取枢轴,对列表进行分区, fork 生成器供两次递归调用使用,递归排序左右两边,然后全部拼接在一起。
方便的功能很容易从这里编写
qSortBy f xs = qSortByGen (genFromHashable xs) f xs
qSort = qSortBy compare
注意最终函数的签名。
ghci> :t qSort
qSort :: (Ord a, Hashable a) => [a] -> [a]
列表中的类型必须同时实现Hashable
和Ord
。有您要求的“纯”功能,还有一个合乎逻辑的附加要求。更通用的功能对其要求的限制较少。
ghci> :t qSortBy
qSortBy :: (Hashable a) => (a -> a -> Ordering) -> [a] -> [a]
ghci> :t qSortByGen
qSortByGen
:: (System.Random.RandomGen t) =>
t -> (a -> a -> Ordering) -> [a] -> [a]
最后的笔记
qSort
所有输入的行为方式完全相同。“随机”枢轴选择是。事实上,确定性. 但是它通过散列列表然后播种随机数生成器而变得模糊,使其对我来说足够“随机”。;)
qSort
也只适用于长度小于的列表maxBound :: Int
,ghci 告诉我是 9,223,372,036,854,775,807。我认为负索引会有问题,但在我的临时测试中我还没有遇到它。
或者,您可以只使用 IO 单子以获得“更真实”的随机性。
qSortIO xs = do g <- getStdGen -- add getStdGen to your imports
return $ qSortByGen g compare xs
ghci> :t qSortIO
qSortIO :: (Ord a) => [a] -> IO [a]
ghci> qSortIO "Hello world"
" Hdellloorw"
ghci> qSort "Hello world"
" Hdellloorw"