Генерация списка случайных чисел с помощью foldM - PullRequest
1 голос
/ 15 января 2012

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

Вот что у меня есть:

useRNG nums min = do
    generator <- get
    let (val, generator') = randomR (min, 51) generator
    put generator'
    return $ val : nums

foldM useRNG [] [0 .. 50]

Кто-нибудь может мне помочь?

1 Ответ

4 голосов
/ 15 января 2012

Проблема в том, что useRNG может генерировать все виды чисел (любой экземпляр Random) и работать во всех видах монад (любая монада состояний, состояние которой является экземпляром RandomGen), как это может бытьвидно из его предполагаемой сигнатуры:

GHCi> :t useRNG
useRNG
  :: (MonadState s m, RandomGen s, Random a, Num a) =>
     [a] -> a -> m [a]

... но когда вы используете ее, вы не указали , какие конкретные типы вам действительно нужны.

Есливы устраняете неоднозначность с помощью сигнатуры типа:

test :: State StdGen [Int]
test = foldM useRNG [] [0 .. 50]

, тогда она работает нормально.Вы также можете сделать это, поставив сигнатуру типа на useRNG:

useRNG :: [Int] -> Int -> State StdGen [Int]

Теперь вы можете подумать: если useRNG отлично работает со всеми этими типами, то почему не может test тоже?Ответ - ограничение мономорфизма , которое довольно загадочно и не очень нравится многим пользователям Haskell.Вы можете избежать этого, поместив

{-# LANGUAGE NoMonomorphismRestriction #-}

вверху вашего файла или указав test явную сигнатуру типа:

test :: (RandomGen g, Random a, Num a, Enum a, MonadState g m) => m [a]

Вы можете найти правильную сигнатуру типас GHCi:

GHCi> :t foldM useRNG [] [0 .. 50]
foldM useRNG [] [0 .. 50]
  :: (MonadState s m, RandomGen s, Random b, Num b, Enum b) => m [b]

(я написал явную сигнатуру типа перед проверкой с помощью GHCi, поэтому у меня она немного отличается.)

Однако эта сигнатура типа немного слишком полиморфный для практического использования - вы будете в основном устранять неоднозначность до тех пор, пока не будете фактически использовать результат - поэтому я бы предложил конкретизировать его в этом случае.Например, вы можете оставить test универсальным для типа случайного числа без ненужного полиморфизма над монадой состояния и типом генератора:

test :: (Random a, Num a, Enum a) => State StdGen [a]

Вы также можете рассмотреть возможность использования MonadRandom , который включает все стандартные средства генерации случайных чисел в интерфейсе на основе монад, поэтому вам не нужно:)

...