ランダムソート(笑)をHaskellで

30分プログラム、その527。ランダムソート(笑)Haskellでやってみた。
なんかおもろそうだな、と気楽に始めたら意外と難しかった。

  • Haskellで乱数といえば、Stateモナドだよな
  • Stateがいるから、Data.Listのsortが使えない -> sortの再実装
  • 比較関数がStateだから、filterが使えない-> filterの再実装

みたいな感じで再実装しないといけない部分が多くて大変だった。

最終的に

randomSort :: [a] -> IO [a]

という、それっぽい型の関数が得られたので満足です。

使い方

*Main Data.List> randomSort [1..10]
[9,10,1,7,5,2,8,3,4,6]
*Main Data.List> randomSort [1..10]
[7,4,9,2,5,1,10,8,6,3]
*Main Data.List> randomSort [1..10]
[10,2,5,1,6,3,4,9,8,7]

ソースコード

import System.Random
import Control.Monad.State
import Data.List

randomCmp :: RandomGen g => a -> a -> State g Bool
randomCmp _ _ = do g <- get
                   (x,g') <- return $ random g
                   put g'
                   return x

filterS :: (a -> State b Bool) -> [a] -> State b [a]
filterS _ [] = do return []
filterS f (x:xs) = do ok <- f x
                      ys <- filterS f xs
                      if ok
                        then return (x:ys)
                        else return ys

spanS :: (a -> State b Bool) -> [a] -> State b ([a],[a])
spanS _ [] = do return ([],[])
spanS f (x:xs) = do ok <- f x
                    (ys,zs) <- spanS f xs
                    if ok
                      then return (x:ys,zs)
                      else return (ys  ,x:zs)

qsortS :: (a -> a -> State b Bool) -> [a] -> State b [a]
qsortS _ [] = do return []
qsortS less (x:xs) = do (ys,zs) <- spanS (\b -> less x b) xs
                        ys' <- qsortS less ys
                        zs' <- qsortS less zs
                        return $ ys' ++ [x] ++ zs'

randomSort xs = do g <- newStdGen
                   return $ fst $ runState (qsortS randomCmp xs) g