問題 25

(初級 🌟) リストの要素のランダムな順列を生成せよ。

variable {α : Type} [Inhabited α] [BEq α]

/-- 与えられたリストからランダムに1要素を取り除き、その要素と残りのリストを返す -/
def List.extractOne (univ : List α) : IO (Option α × List α) := do
  if univ == [] then
    return (none, [])

  let index ← IO.rand 0 (univ.length - 1)
  let element := univ[index]!
  let rest := univ.take index ++ univ.drop (index + 1)
  return (element, rest)

partial def rndPermu (l : List α) : IO (List α) := do
  let (element, rest) ← l.extractOne
  match element with
  | none => return []
  | some e =>
    return e :: (← rndPermu rest)

-- テスト例(#eval で確認)
def runTest [ToString α] (l : List α) : IO Unit := do
  let result ← rndPermu l
  let check := result.isPerm l

  if l.length >= 30 then
    let result' ← rndPermu l
    if result == result' then
      throw <| .userError "failed: Your function is not random."

  if check then
    IO.println "ok!"
  else
    throw <| .userError s!"failed: rndPermu {l} = {result}"

#eval runTest [1, 2, 3]
#eval runTest ['a', 'a', 'a']
#eval runTest ([] : List Nat)
#eval runTest (List.range 35)