Я хочу сделать функцию Haskell, которая может выбрать случайное число из данного списка. Моя подпись типа:
randomPick :: [a] -> a
Что мне делать?
Я хочу сделать функцию Haskell, которая может выбрать случайное число из данного списка. Моя подпись типа:
randomPick :: [a] -> a
Что мне делать?
Часть определения "чистой" функции в Haskell заключается в том, что она ссылочно прозрачная, то есть, взаимозаменяемый с результатом его оценки. Это означает, что результат его оценки должен быть одинаковым каждый раз. Я боюсь, Эрго, функция, которую ты хочешь, невозможен. Чтобы генерировать случайные числа в Haskell, функция должна выполнять одну из двух задач:
Возьмите и верните генератор псевдослучайных чисел, например:
randomPick :: RNG -> [a] -> (a, RNG)
Или используйте IO
для доступа к случайности из "внешнего мира":
randomPick :: [a] -> IO a
Оба стиля предоставляются модулем System.Random
. Кроме того, в первом случае передача PRNG вокруг может быть абстрагирована с использованием монады State
или, возможно, особой беспорядочной монады.
То, что вы описали, не может быть выполнено в чистом функциональном коде.
Чистый функциональный код подразумевает, что вы будете получать одинаковый вывод для одного и того же ввода каждый раз. Поскольку функция рандомизации по определению дает вам разные выходные данные для одного и того же входа, это невозможно в чистом функциональном коде.
Если вы не передадите дополнительное значение, как описано в @camccann answer. Технически, это даже не должно быть таким передовым, как RNG, в зависимости от ваших потребностей. Вы можете передать целое число и умножить его на 10 и вычесть 3 (или что-то еще), а затем по модулю этого найти свой индекс. Тогда ваша функция остается чистой, но вы непосредственно контролируете случайность.
Другой вариант - использовать RandomRIO
для создания числа в диапазоне, которое затем можно использовать для выбора индекса из список. Для этого вам потребуется ввести монаду IO.
Если вы хотите использовать генераторы случайных чисел в чисто функциональном коде, но не должны явно передавать состояние генератора, вы можете использовать государственную монаду (или монадный трансформатор) и скрыть водопровод. Государственные монады по-прежнему по-видимому прозрачны и безопасны и нормальны, чтобы избежать государственной монады. Вы также можете использовать монаду 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