Как я могу генерировать случайные числа в Haskell без IO в диапазоне? - PullRequest
0 голосов
/ 01 октября 2019

Я хотел бы генерировать случайные числа в диапазоне, а сигнатура типа должна быть Int -> Int. Я прочитал несколько других постов, но ни один из них не предложил способы вернуть тип Int. Я использовал System.IO.Unsafe в своем коде, но это делать не рекомендуется. Вот мой код:

import System.IO.Unsafe

-- random number generator
rng :: Int -> Int
rng upper = unsafePerformIO $ randomRIO (0,upper-1) 

Есть ли у кого-нибудь предложения о том, как генерировать случайные значения Int в диапазоне в Haskell?

Редактировать: Возможно, изменить IO Int -> Int может быть невозможно, поэтому я преобразовалмой код для

-- random number generator
rng :: Int -> IO Int
rng upper = randomRIO (0,upper-1) 

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

list !! rng (length list) но я получаю ошибку Couldn't match expected type ‘Int’ with actual type ‘IO Int’, которая ожидается.

Это не дубликат, потому что 1. я хочу значения в диапазоне, 2. мой rng не возвращает те же значения. Я новичок в Хаскеле и не знаю, как манипулировать Монадой. Любая помощь приветствуется.

Ответы [ 2 ]

6 голосов
/ 01 октября 2019

В духе https://xkcd.com/221/, вот «решение» без какого-либо ввода-вывода:

rng :: Int -> Int
rng upper
 | upper<=4   = upper
 | otherwise  = 4

Так что вы получите «случайное число, соответствующее RFC 1149.5». Всегда четыре, если только это не выходит за пределы диапазона.

В чем проблема? Ну, ясно, что он всегда дает одно и то же число - и поэтому должно быть , потому что все функции Haskell должны быть функциями , то есть ссылочно-прозрачными . OTOH, случайное число генератор должен давать различное число каждый раз, когда вы вызываете его ... таким образом, не функция, и большинство других языков программированияпросто притвориться, что это функция с побочным эффектом - потому что у них нет подходящих средств для выражения того, что такое побочные эффекты. Что ж, у Haskell есть подходящие средства для выражения этого, и это монада IO: у вас могут быть вычисления, которые зависят от побочного эффекта, но ясно, что эти вычисления, если вы их запустите, сами будут иметь этот побочный эффект.
В этом свете подпись Int -> IO Int имеет смысл для функции. (Это является функцией, но результатом является IO-действие и только при выполнении это действие дает вам Int.)

Уродливо то, что IO Int может буквально сделать что угодно в IO - например, может запустить несколько ракет и вернуть вам количество жертв. Более реалистично, это могло легко изменить некоторый файл в Вашей домашней директории. Принимая во внимание, что вы на самом деле хотите всего лишь крошечный безобидный побочный эффект, которого достаточно, чтобы в следующий раз получить новое случайное число. Обычно генераторы случайных чисел в любом случае не являются случайными, а PRNG , которые сохраняют постоянную переменную состояния , которая обновляется случайным образом каждый раз, когда вы извлекаете значение. В следующий раз это состояние будет другим, и вы получите другое значение по желанию. Эта переменная состояния может храниться в IO -mutable месте

import Data.IORef

type RandStV = Int
type RandSt = IORef RandStV

rng' :: RandSt -> Int -> IO Int
rng' rSt upper = do
   x <- readIORef rSt
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff -- https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.26#l362
   writeIORef rSt x'
   return $ x `mod` upper

... или вы можете просто явно передать обновленное состояние вместе с результатом

rng'' :: Int -> RandStV -> (RandStV, Int)
rng'' upper x =
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   in (x', x `mod` upper)

... или его можно передать в выделенной монаде состояния , которая является еще одним способом записи передачи обновленной переменной:

type RandStM = State RandStV

rng''' :: Int -> RandStM Int
rng''' upper = do
   x <- get
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   put x'
   return $ x `mod` upper

См. пакет random-fu для полезных помощников по такой случайной монаде .

Один математический способ интерпретации rng''' состоит в том, чтобы сказать, что это функция, котораяпринимает верхнюю границу в качестве аргумента и возвращает распределение чисел. Распределение всегда одинаково, но оно «содержит» много чисел вместе с вероятностью их появления. На самом деле генерирует целое число означает, что вы выборка из распределения.

2 голосов
/ 01 октября 2019

Haskell не был создан для генерации случайных чисел без использования IO.

Ваш пример list !! rng (length list) не работает, поскольку rng возвращает IO Int, а !! ожидает Int.

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

-- Will crash on empty list
randomElementFromList :: [a] -> IO a
randomElementFromList list = do
  r <- rng (length list)
  return $ list !! r
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...