Подтвердить что ты не робот

Как выбрать элемент случайного списка в чистой функции?

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

randomPick :: [a] -> a 

Что мне делать?

4b9b3361

Ответ 1

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

Возьмите и верните генератор псевдослучайных чисел, например:

randomPick :: RNG -> [a] -> (a, RNG)

Или используйте IO для доступа к случайности из "внешнего мира":

randomPick :: [a] -> IO a

Оба стиля предоставляются модулем System.Random. Кроме того, в первом случае передача PRNG вокруг может быть абстрагирована с использованием монады State или, возможно, особой беспорядочной монады.

Ответ 2

То, что вы описали, не может быть выполнено в чистом функциональном коде.

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

Если вы не передадите дополнительное значение, как описано в @camccann answer. Технически, это даже не должно быть таким передовым, как RNG, в зависимости от ваших потребностей. Вы можете передать целое число и умножить его на 10 и вычесть 3 (или что-то еще), а затем по модулю этого найти свой индекс. Тогда ваша функция остается чистой, но вы непосредственно контролируете случайность.

Другой вариант - использовать RandomRIO для создания числа в диапазоне, которое затем можно использовать для выбора индекса из список. Для этого вам потребуется ввести монаду IO.

Ответ 3

Если вы хотите использовать генераторы случайных чисел в чисто функциональном коде, но не должны явно передавать состояние генератора, вы можете использовать государственную монаду (или монадный трансформатор) и скрыть водопровод. Государственные монады по-прежнему по-видимому прозрачны и безопасны и нормальны, чтобы избежать государственной монады. Вы также можете использовать монаду 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