Как выбрать случайный элемент списка в чистой функции? - PullRequest
19 голосов
/ 28 мая 2010

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

randomPick :: [a] -> a 

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

Ответы [ 3 ]

26 голосов
/ 28 мая 2010

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

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

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

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

randomPick :: [a] -> IO a

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

22 голосов
/ 28 мая 2010

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

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

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

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

5 голосов
/ 28 мая 2010

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