Улучшение кода для генерации дистрибутива - PullRequest
5 голосов
/ 07 августа 2010

Я новичок в Haskell, и мне интересно, как / если я могу сделать этот код более эффективным и аккуратным. Это кажется излишне длинным и неопрятным.

Мой скрипт генерирует список из 10 средних значений по 10 бросков монет.

import Data.List
import System.Random

type Rand a = StdGen -> Maybe (a,StdGen)

output = do
    gen <- newStdGen
    return $ distBernoulli 10 10 gen

distBernoulli :: Int -> Int -> StdGen -> [Double]
distBernoulli m n gen = [fromIntegral (sum x) / fromIntegral (length x) | x <- lst]
    where lst = splitList (randomList (n*m) gen) n

splitList :: [Int] -> Int -> [[Int]]
splitList [] n = []
splitList lst n = take n lst : splitList (drop n lst) n

randomList :: Int -> StdGen -> [Int]
randomList n = take n . unfoldr trialBernoulli

trialBernoulli :: Rand Int
trialBernoulli gen = Just ((2*x)-1,y)
                 where (x,y) = randomR (0,1) gen

Любая помощь будет оценена, спасибо.

Ответы [ 4 ]

3 голосов
/ 07 августа 2010

Я бы решил эту проблему немного иначе. Сначала я определил бы функцию, которая дала бы мне бесконечную выборку сальто из распределения Бернулли с вероятностью успеха p:

flips :: Double -> StdGen -> [Bool]
flips p = map (< p) . randoms

Тогда я бы написал distBernoulli следующим образом:

distBernoulli :: Int -> Int -> StdGen -> [Double]
distBernoulli m n = take m . map avg . splitEvery n . map val . flips 0.5
  where
    val True = 1
    val False = -1
    avg = (/ fromIntegral n) . sum

Я думаю, что это соответствует вашему определению distBernoulli:

*Main> distBernoulli 10 10 $ mkStdGen 0
[-0.2,0.4,0.4,0.0,0.0,0.2,0.0,0.6,0.2,0.0]

(Обратите внимание, что я использую splitEvery из удобного пакета split , поэтому вам нужно будет установить пакет и добавить import Data.List.Split (splitEvery) в ваш импорт.)

Этот подход немного более общий, и я думаю, что немного более аккуратный, но на самом деле главное отличие состоит в том, что я использую randoms и splitEvery.

2 голосов
/ 07 августа 2010

РЕДАКТИРОВАТЬ: я разместил это слишком быстро и не соответствует поведению, теперь должно быть хорошо.

import Control.Monad.Random
import Control.Monad (liftM, replicateM)

ЗНАНИЕ: Если вам нравятся случайные люди, тогда используйте MonadRandom - он качается.

STYLE: только импорт используемых вами символов улучшает удобочитаемость, а иногда и удобство обслуживания.

output :: IO [Double]
output = liftM (map dist) getLists

Примечание: я дал выводу явный тип, но знаю, что не имеет для ввода-вывода.

ТИП:

1) Обычно хорошо отделять ваш IO от чистых функций. Здесь я разделил получение случайных списков из расчета распределений. В вашем случае это было чисто, но вы комбинировали получение «случайных» списков через генератор с функцией распределения; Я бы разделил эти части.

2) Читать Обозначения, считающиеся вредными . Попробуйте использовать >>= вместо

output = do
   gen <- new
   return $ dist gen

вы можете сделать:

output = new >>= dist

Вау!

dist :: [Int] -> Double
dist lst = (fromIntegral (sum lst) / fromIntegral (length lst))

getLists :: MonadRandom m => Int -> Int -> m [[Int]]
getLists m n= replicateM m (getList n)

ЗНАНИЕ В Control.Monad все, что заканчивается M, похоже на оригинал, но для монад. В этом случае replicateM должно быть знакомо, если вы использовали функцию Data.List replicate.

getList :: MonadRandom m => Int -> m [Int]
getList m = liftM (map (subtract 1 . (*2)) . take m) (getRandomRs (0,1::Int))

STYLE: Если я делаю что-то много раз, мне нравится иметь один экземпляр в его собственной функции (getList), тогда повторение в отдельной функции.

0 голосов
/ 07 августа 2010

Используя вышеизложенное, я теперь использую это.

import Data.List
import System.Random

type Rand a = [a]

distBernoulli :: Int -> Int -> StdGen -> [Double]
distBernoulli m n gen = [fromIntegral (sum x) / fromIntegral (length x) | x <- lst]
    where lst = take m $ splitList (listBernoulli gen) n

listBernoulli :: StdGen -> Rand Int
listBernoulli = map (\x -> (x*2)-1) . randomRs (0,1)

splitList :: [Int] -> Int -> [[Int]]
splitList lst n = take n lst : splitList (drop n lst) n

Спасибо за вашу помощь, и я приветствую любые дальнейшие комментарии:)

0 голосов
/ 07 августа 2010

Я не уверен, что понимаю ваш код или ваш вопрос ...

Но мне кажется, все, что вам нужно сделать, это сгенерировать список случайных единиц и нулей, а затем разделить каждого из них по длине на map и сложить их вместе с foldl.

Что-то вроде:

makeList n lis = если n / = 0, то makeList (n-1) randomR (0,1): lis еще лис

А затем заставьте его применить Карту и Foldl или Foldr.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...