Рекурсия Haskell со случайными числами и вводом-выводом

Для 99 вопросов Haskell, особенно для 23-го , мне нужно to

"Извлечь заданное количество случайно выбранных элементов из списка.

Пример (на лиспе):

(rnd-select '(a b c d e f g h) 3)
(E D A)

"

Что я реализовал так:

import System.Random
import Control.Monad

removeAt :: [a] -> Int -> [a]
removeAt (x:xs) i
    | i > 0  = x : removeAt xs (i-1)
    | otherwise = xs

rndSelect :: (RandomGen g) => [a] -> Int ->  g -> IO [a]
rndSelect _ 0 _ = return []
rndSelect xs n gen = do
    let (pos, newGen) = randomR (0, length xs - 1) gen
    rest <- rndSelect (removeAt xs pos) (n-1) newGen
    return $ (xs!!pos):rest

-- for an explanation of what this is doing see EXPLANATION below

Насколько я могу судить, это работает, но меня беспокоят последние две строки. Я ' m новичок в этом, и я не знаю связанных затрат, связанных с оператором '<-', который многократно входит и выходит из ввода-вывода, как я делаю. Насколько это эффективно, есть ли лучший способ сделать это, который не делает это? Это связано с отскоком ввода-вывода, или нет никаких реальных накладных расходов?

Любое понимание, которое у вас есть, приветствуется, так как я только недавно начал изучать эти более сложные концепции в Haskell и еще не привык рассуждать о системе ввода-вывода Haskell .

ОБЪЯСНЕНИЕ: Для этого я решил, что я должен случайным образом выбрать один элемент из списка с помощью функции randomR (возвращает случайное число в заданном диапазоне) и продолжать делать это рекурсивно, пока я не возьму n элементы.

Я сделал несколько предположений о проблеме, которые привели меня к такому подходу. Во-первых, я предположил, что rndSelect может выбрать определенный элемент из списка только один раз, а во-вторых, я предположил, что каждый элемент должен иметь равную вероятность быть выбранным.

PS: это мой первый вопрос по SO, поэтому, если я плохо отформатировал вопрос, не стесняйтесь сказать мне.

5
задан Dave 10 May 2011 в 05:13
поделиться