Purescriptメモ - 配列のシャッフルを様々な方法で実装する
PureScriptで配列のシャッフルをしたい。型はこんな感じ。乱数は副作用を伴うため、返り値の型はEffectで包まれる。 1 shuffle :: forall a. Array a -> Effect (Array a) アルゴリズムはFisher-Yates ShuffleのModern Algorithmの項の2つ目を利用する。これをさまざまな方法で作成したところ、Functor, Applicative, Monadなどに関連する事項だったり、STモナドの使い方、FFIの使い方だったりが学べたので、備忘のために書く。 準備 適当なディレクトリでプロジェクトを作成する。今回使うパッケージをインストールする。 $ spago init $ spago install arrays $ spago install random $ spago install foldable-traversable 方法1: 素直(?)な書き方 ここでは、src/Shuffle.pursに記述する。 天下り的ではあるが、これから使う関数、型をimportしておく。 1 2 3 4 5 6 7 8 9 10 module Shuffle where import Prelude import Effect (Effect) import Data.Array (range, (!!), updateAt, length) import Data.Traversable (for) import Effect.Random (randomInt) import Data.Maybe (maybe) import Data.Foldable (foldr) まずは、「どの添字ととの添字の値を交換するか」という情報をもったデータExchangeIndexと、それを作成する関数exchangeIndiciesを作成する。 1 2 3 4 5 6 7 8 9 10 type ExchangeIndex = { i :: Int , j :: Int } exchangeIndicies :: Int -> Effect (Array ExchangeIndex) exchangeIndicies n = for (range 0 (n - 2)) \i -> do j <- randomInt i (n - 1) pure { i, j } 次に、ExchangeIndexの情報を元に配列を交換する関数exchangeを作成。配列の添字が不正だった場合(配列外参照を起こしそうなとき)は!!演算子がNothingを返すため、一連の計算はMaybeに包まれる。今回は簡単のため、交換に失敗したら元の配列をそのまま返すような実装にする。 ...