I finally decided to actually solve the problem, and I'm sorry to say I
was on the wrong track. ListT won't do it on its own: you actually need
a custom monad that does the random pick in the bind operation.
Attached are a module to solve the problem and a Main module that tests
it. I hope this helps.
Paul.
-- | Test the MonteCarlo Monad
module Main where
import System.Random
import MonteCarlo
f, g :: Int -> MonteCarlo Int
f x = returnList $ map (*3) [x .. x+( x `mod` 5)-1]
g x = returnList $ map (*2) [x .. x+( x `mod` 7)-1]
-- The actual Monte-Carlo computation
experiment :: [Int] -> MonteCarlo (Int, Int, Int)
experiment xs = do
x <- returnList xs
f1 <- f x
g1 <- g x
return (x, f1, g1)
-- Infinite list of generators
generators :: StdGen -> [StdGen]
generators g = g1 : generators g2
where (g1, g2) = split g
main :: IO ()
main = do
g <- getStdGen
print $ take 10 $ map (runMonteCarlo $ experiment [1..100]) $ generators g
-- | A monad of random non-determinism. Each action in the computation
-- generates zero or more results. Zero is failure, but if more than one
-- result is returned then one is selected at random.
module MonteCarlo (
MonteCarlo,
runMonteCarlo,
returnList
) where
import Control.Monad
import System.Random
newtype MonteCarlo a = MonteCarlo {runMC :: StdGen -> (StdGen, [a])}
-- | Run a Monte-Carlo simulation to generate a zero or one results.
runMonteCarlo :: MonteCarlo a -> StdGen -> Maybe a
runMonteCarlo (MonteCarlo m) g1 =
let
(g2, xs) = m g1
(_, x) = pickOne xs g2
in case xs of
[] -> Nothing
[x1] -> Just x1
_ -> Just x
-- Internal function to pick a random element from a list
pickOne :: [a] -> StdGen -> (StdGen, a)
pickOne xs g1 = let (n, g2) = randomR (0, length xs - 1) g1 in (g2, xs !! n)
instance Monad MonteCarlo where
MonteCarlo m >>= f = MonteCarlo $ \g1 ->
let -- If I was clever I'd find a way to merge this with runMonteCarlo.
(g2, xs) = m g1
(g3, x) = pickOne xs g2
f' = case xs of
[] -> mzero
[x1] -> f x1
_ -> f x
in runMC f' g3
return x = MonteCarlo $ \g -> (g, [x])
instance MonadPlus MonteCarlo where
mzero = MonteCarlo $ \g -> (g, [])
mplus (MonteCarlo m1) (MonteCarlo m2) = MonteCarlo $ \g ->
let
(g1, xs1) = m1 g
(g2, xs2) = m2 g1
in (g2, xs1 ++ xs2)
-- | Convert a list of items into a Monte-Carlo action.
returnList :: [a] -> MonteCarlo a
returnList xs = MonteCarlo $ \g -> (g, xs)
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe