From a0ebfb83a37e0cce84d9d2699291de5cbb04abc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Depreeuw?= Date: Fri, 10 Nov 2017 19:39:19 +0100 Subject: [PATCH] Complete chapter 23 --- 23-state/23.5-roll-your-own.md | 2 ++ 23-state/23.8-chapter-exercises.md | 2 ++ 23-state/src/Moi.hs | 26 ++++++++++++++ 23-state/src/RandomExample.hs | 32 +++++++++++++++++ 23-state/src/RandomExample2.hs | 57 ++++++++++++++++++++++++++++++ 23-state/src/chapter.hs | 35 ++++++++++++++++++ 23-state/src/fizzbuzz.hs | 52 +++++++++++++++++++++++++++ 7 files changed, 206 insertions(+) create mode 100644 23-state/23.5-roll-your-own.md create mode 100644 23-state/23.8-chapter-exercises.md create mode 100644 23-state/src/Moi.hs create mode 100644 23-state/src/RandomExample.hs create mode 100644 23-state/src/RandomExample2.hs create mode 100644 23-state/src/chapter.hs create mode 100644 23-state/src/fizzbuzz.hs diff --git a/23-state/23.5-roll-your-own.md b/23-state/23.5-roll-your-own.md new file mode 100644 index 0000000..169fa52 --- /dev/null +++ b/23-state/23.5-roll-your-own.md @@ -0,0 +1,2 @@ +# Exercises: Roll Your Own +see src/RandomExample2.hs \ No newline at end of file diff --git a/23-state/23.8-chapter-exercises.md b/23-state/23.8-chapter-exercises.md new file mode 100644 index 0000000..02acf4a --- /dev/null +++ b/23-state/23.8-chapter-exercises.md @@ -0,0 +1,2 @@ +# Chapter Exercises +see src/chapter.hs \ No newline at end of file diff --git a/23-state/src/Moi.hs b/23-state/src/Moi.hs new file mode 100644 index 0000000..5c5d563 --- /dev/null +++ b/23-state/src/Moi.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE InstanceSigs #-} +module Moi where + +newtype Moi s a = Moi { runMoi :: s -> (a, s) } + +instance Functor (Moi s) where + fmap :: (a -> b) -> Moi s a -> Moi s b + fmap f (Moi g) = Moi $ \s -> let (a, s') = g s + in (f a, s') + +instance Applicative (Moi s) where + pure :: a -> Moi s a + pure a = Moi $ \s -> (a, s) + + (<*>) :: Moi s (a -> b) -> Moi s a -> Moi s b + (Moi f) <*> (Moi g) = Moi $ \s -> let (a, s') = g s + (f', s'') = f s' + in (f' a, s'') + +instance Monad (Moi s) where + return :: a -> Moi s a + return = pure + + (>>=) :: Moi s a -> (a -> Moi s b) -> Moi s b + (Moi f) >>= g = Moi $ \s -> let (a, s') = f s + in runMoi (g a) s' \ No newline at end of file diff --git a/23-state/src/RandomExample.hs b/23-state/src/RandomExample.hs new file mode 100644 index 0000000..b0375c2 --- /dev/null +++ b/23-state/src/RandomExample.hs @@ -0,0 +1,32 @@ +module RandomExample where + +import System.Random + +-- Six-sided die +data Die = + DieOne + | DieTwo + | DieThree + | DieFour + | DieFive + | DieSix + deriving (Eq, Show) + +intToDie :: Int -> Die +intToDie n = case n of + 1 -> DieOne + 2 -> DieTwo + 3 -> DieThree + 4 -> DieFour + 5 -> DieFive + 6 -> DieSix + -- Use 'error' _extremely_ sparingly + x -> error $ "intToDie got non 1-6 integer: " ++ show x + +rollDieThreeTimes :: (Die, Die, Die) +rollDieThreeTimes = do + let s = mkStdGen 0 + (d1, s1) = randomR (1, 6) s + (d2, s2) = randomR (1, 6) s1 + (d3, _) = randomR (1, 6) s2 + (intToDie d1, intToDie d2, intToDie d3) \ No newline at end of file diff --git a/23-state/src/RandomExample2.hs b/23-state/src/RandomExample2.hs new file mode 100644 index 0000000..1e8ecd2 --- /dev/null +++ b/23-state/src/RandomExample2.hs @@ -0,0 +1,57 @@ +module RandomExample2 where + +import Control.Applicative (liftA3) +import Control.Monad (replicateM) +import Control.Monad.Trans.State +import System.Random +import RandomExample + + +rollDie :: State StdGen Die +rollDie = state $ do + (n, s) <- randomR (1, 6) + return (intToDie n, s) + +rollDie' :: State StdGen Die +rollDie' = intToDie <$> state (randomR (1, 6)) + +rollDieThreeTimes' :: State StdGen (Die, Die, Die) +rollDieThreeTimes' = liftA3 (,,) rollDie' rollDie' rollDie' + +infiniteDie :: State StdGen [Die] +infiniteDie = repeat <$> rollDie' + +nDie :: Int -> State StdGen [Die] +nDie n = replicateM n rollDie + +rollsToGetTwenty :: StdGen -> Int +rollsToGetTwenty g = go 0 0 g + where go :: Int -> Int -> StdGen -> Int + go sum' count gen + | sum' >= 20 = count + | otherwise = let (die, nextGen) = randomR (1, 6) gen + in go (sum' + die) (count + 1) nextGen + +-- Exercises: Roll Your Own +-- 1 +rollsToGetN :: Int -> StdGen -> Int +rollsToGetN n g = go 0 0 g + where go :: Int -> Int -> StdGen -> Int + go sum' count gen + | sum' >= n = count + | otherwise = let (die, nextGen) = randomR (1, 6) gen + in go (sum' + die) (count + 1) nextGen + + -- 2 +rollsCountLogged :: Int -> StdGen -> (Int, [Die]) +rollsCountLogged n g = go 0 (0, []) g + where go :: Int -> (Int, [Die]) -> StdGen -> (Int, [Die]) + go sum' r@(count, dice) gen + | sum' >= n = r + | otherwise = let (die, nextGen) = randomR (1, 6) gen + in go (sum' + die) + ((count + 1), intToDie die : dice) + nextGen + +rollsToGetN' :: Int -> StdGen -> Int +rollsToGetN' n = fst . rollsCountLogged n \ No newline at end of file diff --git a/23-state/src/chapter.hs b/23-state/src/chapter.hs new file mode 100644 index 0000000..9506f8a --- /dev/null +++ b/23-state/src/chapter.hs @@ -0,0 +1,35 @@ +module Chapter where + +newtype State s a = State { runState :: s -> (a, s) } +instance Functor (State s) where + fmap f k = State $ \s -> let (a, s') = runState k s + in (f a, s') +instance Applicative (State s) where + pure a = State $ \s -> (a, s) + (State f) <*> k = State $ \s -> let (a, s') = runState k s + (f', s'') = f s' + in (f' a, s'') +instance Monad (State s) where + return = pure + (State f) >>= k = State $ \s -> let (a, s') = f s + in runState (k a) s' + +-- 1 +get :: State s s +get = State $ \s -> (s,s) + +-- 2 +put :: s -> State s () +put s = State $ \_ -> ((),s) + +-- 3 +exec :: State s a -> s -> s +exec st = snd . runState st + +-- 4 +eval :: State s a -> s -> a +eval st = fst . runState st + +-- 5 +modify :: (s -> s) -> State s () +modify f = State $ \s -> ((), f s) diff --git a/23-state/src/fizzbuzz.hs b/23-state/src/fizzbuzz.hs new file mode 100644 index 0000000..eb984ad --- /dev/null +++ b/23-state/src/fizzbuzz.hs @@ -0,0 +1,52 @@ +module FizzBuzz where + +import Control.Monad +import Control.Monad.Trans.State +-- http://hackage.haskell.org/package/dlist +-- import qualified Data.DList as DL + +fizzBuzz :: Integer -> String +fizzBuzz n | n `mod` 15 == 0 = "FizzBuzz" + | n `mod` 5 == 0 = "Buzz" + | n `mod` 3 == 0 = "Fizz" + | otherwise = show n + +-- fizzbuzzlist :: [Integer] -> DL.DList String +-- fizzbuzzlist list = execState (mapM_ addResult list) DL.empty + +-- addResult :: Integer -> State (DL.DList String) () +-- addResult n = do +-- xs <- get +-- let result = fizzBuzz n +-- put (DL.snoc xs result) + +-- main :: IO () +-- main = mapM_ putStrLn $ fizzbuzzlist [1..100] + +fizzbuzzlist :: [Integer] -> [String] +fizzbuzzlist list = execState (mapM_ addResult list) [] + +addResult :: Integer -> State [String] () +addResult n = do + xs <- get + let result = fizzBuzz n + put (result : xs) + +-- they all seem to take about the same amount of time... +main :: IO () +-- main = mapM_ putStrLn $ reverse $ fizzbuzzlist [1..100] +main = mapM_ putStrLn $ fizzbuzzFromTo 1 500000 +-- main = mapM_ putStrLn $ fizzbuzzFromTo' 1 500000 + +fizzbuzzFromTo :: Integer -> Integer -> [String] +fizzbuzzFromTo from to = go from to [] + where go f t st + | f >= t = st + | otherwise = execState (addResult f) (go (f+1) t st) + +fizzbuzzFromTo' :: Integer -> Integer -> [String] +fizzbuzzFromTo' from to = execState (mapM_ addResult list) [] + where list = go from to [] + go f t l + | f >= t = l + | otherwise = go (f+1) t (f:l) \ No newline at end of file