我是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
任何帮助都将不胜感激,谢谢。
答案 0 :(得分:3)
我会以稍微不同的方式解决这个问题。首先,我将定义一个函数,该函数可以从伯努利分布中获得无限的翻转样本,成功概率为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]
(请注意,我在方便的split包中使用splitEvery
,因此您必须安装该软件包并将import Data.List.Split (splitEvery)
添加到您的导入中。)
这种方法略胜一筹,我觉得有点整洁,但主要区别在于我正在使用randoms
和splitEvery
。
答案 1 :(得分:2)
import Control.Monad.Random
import Control.Monad (liftM, replicateM)
知识:如果你喜欢randoms然后使用MonadRandom - 它会摇滚。
STYLE:只导入您使用的符号有助于提高可读性和可维护性。
output :: IO [Double]
output = liftM (map dist) getLists
注意:我给输出一个显式类型,但知道它 不是IO。
型:
1)将IO与纯函数分开通常很好。在这里,我分析了从分布计算中获取随机列表。在你的情况下它是纯粹的,但你结合通过发电机获得“随机”列表与分配功能;我会将这些部分分开。
2)阅读Do notation considered harmful。请考虑使用>>=
代替
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
结尾的任何内容都与原始内容相似,但对于monad而言。在这种情况下,如果您使用Data.List replicateM
函数,replicate
应该很熟悉。
getList :: MonadRandom m => Int -> m [Int]
getList m = liftM (map (subtract 1 . (*2)) . take m) (getRandomRs (0,1::Int))
STYLE:如果我多次做某事,我喜欢在自己的函数(getList)中有一个实例,然后在一个单独的函数中重复。
答案 2 :(得分:0)
我不确定我理解您的代码或您的问题......
但在我看来,你需要做的就是生成一个随机的1和0的列表,然后用map
将它们的长度除以它们,并将它们与{{1}一起添加}}
类似的东西:
makeList n lis = if n / = 0 makeList(n-1)randomR(0,1):lis 其他 LIS
然后让它应用Map和Foldl或Foldr。
答案 3 :(得分:0)
使用上述内容,我现在正在使用它。
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
感谢您的帮助,我欢迎任何进一步的评论:)