r/haskell•
prefacer

Learning Monads

I've been toying for Haskell for the past few months and am ready to take the next step. How would you improve the following code using monads? In particular, how can you avoid passing around the random variable seed?

module MontyHallProblem where


import System.Random
import Text.Printf 
import Data.List


main :: IO ()
main = do
  let doors = [1..3]  -- You can change the total number of doors or their labels
  let m = 5           -- Number of game results to display
  let n = 1000000     -- Number of games to play

  g <- newStdGen

  let (switchWins, switchStrs, g') = playN n True  doors g
  let (stayWins,   stayStrs,   g') = playN n False doors g

  putStrLn $ "ALWAYS SWITCH\n" ++ (getSummary switchWins switchStrs m n)
  putStrLn $ "ALWAYS STAY\n"   ++ (getSummary stayWins   stayStrs   m n)


choose :: (Eq a, RandomGen g) => [a] -> g -> (a, g)
choose xs g = (xs!!i, g') where (i, g') = randomR (0, length xs-1) g


excluding :: (Eq a) => [a] -> [a] -> [a]
excluding xs ys = filter (`notElem` ys) xs


play :: (Eq a, Show a, RandomGen g) => Bool-> [a] -> g -> (Bool, String, g)
play willSwitch doors g1
  | willSwitch == True && repick == car = (True,  strSwitch ++ "won!",  g5)
  | willSwitch == True                  = (False, strSwitch ++ "lost!", g5)
  | pick == car                         = (True,  strStay   ++ "won!",  g5)
  | otherwise                           = (False, strStay   ++ "lost!", g5)
  where
  (car,    g2) = choose doors g1
  (pick,   g3) = choose doors g2
  (goat,   g4) = choose (doors `excluding` [car, pick] ) g3
  (repick, g5) = choose (doors `excluding` [pick, goat]) g4
  strIntro = printf
    "Door %s had the car. You picked %s and were shown a goat behind %s.\n"
    (show car) (show pick) (show goat)
  strSwitch = strIntro ++ printf "You switched to door %s and " (show repick)
  strStay   = strIntro ++ printf "You stayed with door %s and " (show pick)


playN :: (Eq a, Show a, RandomGen g) => Int -> Bool -> [a] -> g -> ([Bool], [String], g) 
playN 0 willSwitch doors g1 = ([]      , [],       g1)
playN n willSwitch doors g1 = (win:wins, str:strs, g3)
  where
  (win,  str,  g2) = play        willSwitch doors g1
  (wins, strs, g3) = playN (n-1) willSwitch doors g2


getSummary :: [Bool] -> [String] -> Int -> Int -> String
getSummary wins strs m n =
  printf "%s\n...\nYou won %i out of %i times.\n"
    (intercalate "\n" $ take m $ strs)
    (length $ filter (== True) wins) n

Posted Jul 16, 2017, 10:16 PM UTC

68
Loading comments…