Я хочу сделать функцию Haskell, которая может выбрать случайное число из данного списка. Моя подпись типа:
randomPick :: [a] -> a
Что мне делать?
То, что вы описали, невозможно сделать в чистом функциональном коде.
Чистый функциональный код подразумевает, что вы всегда будете получать один и тот же результат для одного и того же ввода. Поскольку функция рандомизации по определению дает вам разные выходные данные для одного и того же входа, это невозможно в чистом функциональном коде.
Если вы не передадите дополнительное значение, как описано в ответе @ camccann . Технически он даже не должен быть таким продвинутым, как ГСЧ, в зависимости от ваших потребностей. Вы можете передать целое число, умножить его на 10 и вычесть 3 (или что-то еще), а затем взять его по модулю, чтобы найти свой индекс. Тогда ваша функция останется чистой, но вы напрямую контролируете случайность.
Другой вариант - использовать RandomRIO
для генерации числа в диапазоне, которое затем можно использовать для выбора индекса из списка. Это потребует от вас входа в монаду ввода-вывода.
Если вы хотите использовать генераторы случайных чисел в чисто функциональном коде, но не должны явно передавать состояние генератора, вы можете использовать монаду состояний (или преобразователь монад) и скрыть связь. Монады состояний по-прежнему прозрачны по ссылкам, и уйти от монады состояний безопасно и нормально. Вы также можете использовать монаду ST, если хотите истинное локальное изменяемое состояние, которое чисто функционально снаружи.
Вот полезный код, который я написал и иногда использую:
rand :: (Random a, RandomGen g, MonadState g m) => a -> a -> m a
rand lo hi = do
r <- get
let (val, r') = randomR (lo, hi) r
put r'
return val
Часть определения «чистой» функции в Haskell состоит в том, что она референциально прозрачна , то есть взаимозаменяема с результатом ее вычисления. Это означает, что результат его оценки должен быть каждый раз одинаковым. Боюсь, желаемая функция невозможна.Чтобы сгенерировать случайные числа в Haskell, функция должна делать одно из двух:
Принимать и возвращать генератор псевдослучайных чисел, например:
randomPick :: RNG -> [a] -> (a, RNG)
Или использовать IO
для доступа к случайности из «внешнего мира» ":
randomPick :: [a] -> IO a
Оба стиля предоставляются модулем System.Random
. Кроме того, в первом случае передача PRNG может быть абстрагирована с помощью монады State
или, возможно, монады специального назначения Random .