2010-08-06 7 views
5

Je suis nouveau à Haskell et je me demande comment/si je peux rendre ce code plus efficace et plus ordonné. Il semble inutilement long et désordonné.Amélioration du code pour générer une distribution

Mon script génère une liste de 10 moyennes de 10 retournements de pièces.

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 

Toute aide serait appréciée, merci.

Répondre

3

Je traiterais ce problème d'une manière légèrement différente. D'abord, je définir une fonction qui me donnerait un échantillonnage infini de flips d'une distribution Bernoulli avec une probabilité de succès p:

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

J'écrirait distBernoulli comme suit:

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 

Je pense cela correspond à votre définition de 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] 

(Notez que j'utilise splitEvery de l'ha ndy package split, vous auriez donc avoir à installer le package et ajouter import Data.List.Split (splitEvery) à vos importations.)

Cette approche est un peu plus générale, et je pense un peu plus propre, mais vraiment la différence principale est juste que je suis en utilisant randoms et splitEvery.

0

Je ne suis pas sûr que je comprends votre code ou votre question ...

Mais il me semble que vous auriez tout besoin de faire est de générer une liste de ceux aléatoires et des zéros, puis diviser chacun les par leur longueur avec un map et les ajouter avec un foldl.

Quelque chose comme:

makeList n = lis si n/= 0 alors makeList (n-1) randomR (0,1): lis autre lis

Et puis font appliquer une Carte et Foldl ou Foldr à elle.

+0

désolé, je ne l'explique mal. J'essaie fondamentalement de créer les données pour établir la distribution normale standard. en retournant virtuellement une pièce (avec le résultat 1 ou -1) 10 fois, et en prenant la moyenne du résultat, on obtient -0.2. en faisant ce processus disons 1000 fois, nous pouvons tracer les résultats et leur fréquence et obtenir la distribution normale. J'essaie de créer une liste de doubles que je peux tracer pour dessiner cette distribution. – Ash

+0

pour clarifier, le résultat de ce script pourrait être [0.2.0.0,0.0, -0.4,0.6,0.0, -0.2,0.2,0.4,0.0] – Ash

2

EDIT: J'ai posté ceci trop vite et ne correspond pas au comportement, ça devrait être bon maintenant.

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

SAVOIR: Si vous aimez randoms puis utilisez MonadRandom - it rocks. STYLE: Seul l'importation de symboles aide à la lisibilité et parfois à la maintenabilité.

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

Note: J'ai donné la sortie d'un type explicite, mais sachez qu'il ne ont être IO.

STYLE:

1) Il est généralement bon de séparer votre IO de fonctions pures. Ici, j'ai divisé l'obtention de listes aléatoires du calcul des distributions. Dans votre cas c'était pur mais vous avez combiné des listes "aléatoires" via un générateur avec la fonction de distribution; Je diviserais ces parties.

2) Lire Do notation considered harmful. Pensez à utiliser >>= au lieu de

output = do 
    gen <- new 
    return $ dist gen 

vous pouvez faire:

output = new >>= dist 

Wow!

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) 

Connaissance Control.Monad quoi que ce soit se terminant par une M est comme l'original, mais pour monades. Dans ce cas, replicateM devrait être familier si vous avez utilisé la fonction Data.List replicate.

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

STYLE: Si je fais beaucoup quelque chose de fois je voudrais avoir une seule instance dans sa propre fonction (getList), puis la répétition dans une fonction distincte.

0

En utilisant ce qui précède, je l'utilise maintenant.

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 

Merci pour votre aide, et je me réjouis de tout commentaire :)

Questions connexes