配列のシャッフル

30分プログラム、その629。http://gauc.no-ip.org/awk-users-jp/blis.cgi/DoukakuAWK_014nanigac.comにインスパイアされて配列のシャッフルにチャレンジしてみた。

配列のシャッフルはリストの要素のシャッフル - みずぴー日記でもやったけど、上記のサイトでは別の方法でシャッフルを実現してる。

それぞれに1から10までの数値が書かれた10枚のカード。
これをシャッフルするにはどうしたらいいでしょうか。
(中略)
簡単な方法はそれぞれに乱数をくっつけてその乱数でソートすることです。

乱数の振って、それでソートするのはおもしろいね。やってみよう。

使い方

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

ソースコード

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

--shuffle :: [a] -> IO [a]
assignRandom :: RandomGen g => a -> State g (Int,a)
assignRandoms :: RandomGen g => [a] -> State g [(Int,a)]
shuffleS :: RandomGen g => [a] -> State g [a]

assignRandom x = do g <- get
                    (i,g') <- return $ random g
                    put g'
                    return (i,x)
assignRandoms xs = mapM assignRandom xs

shuffleS xs = do ys <- assignRandoms xs
                 let zs = sortBy (\(a,_) (b,_) -> a `compare` b) ys
                 return $ map snd zs

shuffle xs = do g <- newStdGen
                return $ fst $ runState (shuffleS xs) g