2010-08-06 40 views
5

Tôi mới sử dụng Haskell và tôi tự hỏi làm thế nào/nếu tôi có thể làm cho mã này hiệu quả hơn và gọn gàng hơn. Dường như không cần thiết lâu dài và rối rắm.Cải thiện mã để tạo phân phối

Tập lệnh của tôi tạo một danh sách 10 giá trị trung bình là 10 xu lật.

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 

Mọi trợ giúp sẽ được đánh giá cao, cảm ơn.

Trả lời

3

Tôi giải quyết vấn đề này theo một cách hơi khác. Trước tiên tôi muốn xác định một chức năng mà sẽ cung cấp cho tôi một mẫu vô hạn của flips từ một phân phối Bernoulli với thành công khả p:

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

Sau đó, tôi muốn viết distBernoulli như sau:

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 

Tôi nghĩ này phù hợp với định nghĩa của bạn về 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] 

(Lưu ý rằng tôi đang sử dụng splitEvery từ ha ndy split gói, vì vậy bạn phải cài đặt gói và thêm import Data.List.Split (splitEvery) vào hàng nhập của bạn.)

Cách tiếp cận này hơi tổng quát hơn một chút, và tôi nghĩ một chút khác biệt, nhưng thực sự sự khác biệt chính chỉ là tôi sử dụng randomssplitEvery.

0

Tôi không chắc là tôi hiểu mã của bạn hoặc câu hỏi của bạn ...

Nhưng có vẻ như với tôi tất cả các bạn cần làm là tạo ra một danh sách những người ngẫu nhiên và zero, và sau đó chia cho mỗi người trong số theo chiều dài của chúng với map và thêm chúng cùng với foldl.

Cái gì như:

makeList n lis = nếu n/= 0 thì makeList (n-1) randomR (0,1): lis khác lis

Và sau đó làm cho nó áp dụng một Bản đồ và Foldl hoặc Foldr với nó.

+0

xin lỗi, tôi đã giải thích nặng. tôi về cơ bản đang cố tạo dữ liệu để xây dựng bản phân phối chuẩn chuẩn. bằng cách lật một đồng xu (với kết quả 1 hoặc -1) 10 lần, và lấy trung bình của kết quả, chúng ta sẽ nói -0.2. bằng cách thực hiện quá trình này nói 1000 lần, chúng ta có thể vẽ kết quả và tần suất của chúng và thu được phân bố chuẩn. tôi đang cố gắng để tạo ra một danh sách các đôi mà tôi có thể vẽ để phân phối này. – Ash

+0

để làm rõ, kết quả của tập lệnh này có thể là [0,2,0,0,0,0, -0,4,0,6,0,0, -0,2,0,2,0,4,0,0] – Ash

2

CHỈNH SỬA: Tôi đã đăng quá nhanh và không phù hợp với hành vi, giờ đây bạn nên làm tốt.

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

KIẾN THỨC: Nếu bạn thích tiền sử dụng thì sử dụng MonadRandom - đá đó.

PHONG CÁCH: Chỉ nhập các ký hiệu bạn sử dụng giúp dễ đọc và đôi khi có thể bảo trì.

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

Lưu ý: Tôi đã đưa ra một loại rõ ràng, nhưng biết nó không là IO.

PHONG CÁCH:

1) Thường là tốt để tách IO của bạn khỏi các hàm thuần túy. Ở đây tôi đã chia ra nhận được danh sách ngẫu nhiên từ việc tính toán phân phối. Trong trường hợp của bạn nó đã được tinh khiết nhưng bạn kết hợp nhận được danh sách "ngẫu nhiên" thông qua một máy phát điện với chức năng phân phối; Tôi sẽ chia những phần đó ra.

2) Đọc Do notation considered harmful. Xem xét sử dụng >>= thay vì

output = do 
    gen <- new 
    return $ dist gen 

bạn có thể làm:

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) 

KIẾN THỨC Trong Control.Monad bất cứ điều gì kết thúc bằng một M cũng giống như bản gốc nhưng đối với monads. Trong trường hợp này, replicateM nên quen thuộc nếu bạn đã sử dụng chức năng Data.List replicate.

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

PHONG CÁCH: Nếu tôi làm điều gì đó nhiều lần tôi muốn có một cá thể riêng lẻ (getList) thì lặp lại trong một chức năng riêng biệt.

0

Sử dụng ở trên, tôi hiện đang sử dụng tính năng này.

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 

Nhờ sự giúp đỡ của bạn, và tôi hoan nghênh bất kỳ ý kiến ​​thêm :)

Các vấn đề liên quan