Для 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, поэтому, если я плохо отформатировал вопрос, не стесняйтесь сказать мне.