2

Я хочу предоставить функцию, которая заменяет каждое вхождение # в строку с другим случайным числом. На нечистом языке это тривиально. Однако как он должен быть разработан на чистом языке? Я не хочу использовать unsafePerformIO, так как он скорее выглядит как взломанный, а не правильный дизайн.Случайность во вложенной чистой функции

Если эта функция требует случайного генератора в качестве одного из его параметров? И если да, то должен ли этот генератор проходить через весь стек вызовов? Существуют ли другие возможные подходы? Должен ли я использовать монаду State, здесь? Я был бы признателен за игрушечный пример, демонстрирующий жизнеспособный подход ...

+3

Так как ваша первая мысль, чтобы перейти к монадам, вам понравится [MonadRandom] (http://hackage.haskell.org/package/MonadRandom). –

+3

«этот генератор должен пройти через весь стек вызовов» - это хорошо. Я хочу _know_, какие функции нечисты. Функция, использующая случайность, не является чистой; Haskell занимается этим, явно передавая генератор случайных чисел вокруг. –

ответ

2

Фактически, вы использовали вариант государственной монады для передачи случайного генератора за кулисами. Rand тип в Control.Monad.Random помогает с этим. API немного запутан, но больше, потому что он является полиморфным по сравнению с типом случайного генератора, который вы используете, чем потому, что он должен быть функциональным. Однако этот дополнительный бит лесов полезен, потому что вы можете легко повторно использовать свой существующий код с помощью разных случайных генераторов, который позволяет тестировать различные алгоритмы, а также явно контролировать, является ли генератор детерминированным (полезно для тестирования) или засеянным внешними данными (в IO).

Вот простой пример Rand в действии. RandomGen g => в типе подписи говорит нам, что мы можем использовать любой тип генератора случайных чисел. Мы должны явно аннотировать n как Int, потому что в противном случае GHC знает только, что он должен быть числовым типом, который может быть сгенерирован и преобразован в строку, которая может быть одной из нескольких возможных опций (например, Double).

randomReplace :: RandomGen g => String -> Rand g String 
randomReplace = foldM go "" 
    where go str '#' = do 
      n :: Int <- getRandomR (0, 10) 
      return (str ++ show n) 
     go str chr = return $ str ++ [chr] 

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

main :: IO() 
main = do gen <- getStdGen 
      print $ evalRand (randomReplace "ab#c#") gen 

Это такая общая закономерность, что библиотека предоставляет evalRandIO функцию, которая делает это для вас:

main :: IO() 
main = do res <- evalRandIO $ randomReplace "ab#c#" 
      print res 

В конце концов, код немного более ясен о том, что у него есть генератор случайных чисел и передается его, но по-прежнему достаточно легко следовать. Для более привлекательного кода вы также можете использовать RandT, что позволяет расширять другие монады (например, IO) с возможностью генерации случайных значений, позволяя вам отнести все сантехника и настройку к одной части вашего кода.

+1

Вещь, которая раздражает меня в случайности в Haskell, заключается в том, что использование одного и того же кода для псевдослучайных и по-настоящему случайных чисел, по-видимому, в значительной степени требует ленивого ввода-вывода, который широко избегается. Если вы совершаете псевдослучайные числа, а затем решаете переключиться на действительно случайные, все становится довольно неприятным. Возможно, «трубы» или «кабелепровод» или какая-то стрелочная библиотека предлагает хорошее решение; Я не знаю. – dfeuer

+0

спасибо. но как насчет гнездования? теперь вы вызываете эту функцию непосредственно из монады IO. если мы хотим вызвать эту функцию из другой функции, тогда мы должны были бы заставить эту функцию иметь тип X -> Rand g Y, правильно? – piotrek

+1

Да, тип возврата должен быть в «Rand». Преимущество монады состоит в том, что мы можем легко комбинировать функции типа «a -> Rand g b», не делая явную явную основу. Это позволяет нам использовать типы для обозначения того, какие функции зависят от случайности (мы не можем вернуть 'b' непосредственно, потому что он потеряет эту информацию) без излишней стычки при фактическом их объединении. –

0

Это просто Монадическое выполните картирование

import Control.Applicative 
import Control.Monad.Random 
import Data.Char 

randomReplace :: RandomGen g => String -> Rand g String 
randomReplace = mapM f where 
    f '#' = intToDigit <$> getRandomR (0, 10) 
    f c = return c 

main = evalRandIO (randomReplace "#abc#def#") >>= print 

 Смежные вопросы

  • Нет связанных вопросов^_^