Problem Set 1

Random number generation

First exercise is simple. The text says not to try to do anything fancy. Well, I'm not sure what's the opposite of fancy, but it should be my forte. Here a solution:

fiveRands :: [Integer]
fiveRands = [
  fst $ rand $ mkSeed 1,
  fst $ rand $ snd $ rand $ mkSeed 1,
  fst $ rand $ snd $ rand $ snd $ rand $ mkSeed 1,
  fst $ rand $ snd $ rand $ snd $ rand $ snd $ rand $ mkSeed 1,
  fst $ rand $ snd $ rand $ snd $ rand $ snd $ rand $ snd $ rand $ mkSeed 1]

However, it's easy to spot a common pattern in the list of function compositions. A shorter, more general version:

fiveRands' :: [Integer]
fiveRands' = map fst $ take 5 $ iterate (rand . snd) $ rand $ mkSeed 1

I was initially surprised to see it working, since the first element on the array in the first defition is fst $ rand $ mkSeed 1, that is, there's no occurrence of the composite function (rand . snd). But it all makes sense if you observe the definition of iterate.

Random character generation

randLetter :: Seed -> (Char, Seed)
randLetter seed = (toLetter $ fst r, snd r)
  where r = rand seed

randString3 :: String
randString3 = map fst $ take 3 $ iterate (randLetter . snd) $ randLetter $ mkSeed 1

More generators

type Gen a = Seed -> (a, Seed)
myRand :: Gen Integer
myRand = rand

myRandLetter :: Gen Char
myRandLetter = randLetter

generalA :: (a -> b) -> Gen a -> Gen b
generalA f g seed = (f $ fst r, snd r)
  where r = g seed

randEven :: Gen Integer -- the output of rand * 2
randEven = generalA (\x -> x * 2) myRand

randOdd :: Gen Integer -- the output of rand * 2 + 1
randOdd = generalA (\x -> x * 2 + 1) myRand

randTen :: Gen Integer -- the output of rand * 10
randTen = generalA (\x -> x * 10) myRand

Generalizing random pairs

randPair :: Gen (Char, Integer)
randPair seed = ((fst l, fst n), snd n)
  where l = randLetter seed
        n = rand $ snd l

generalPair :: Gen a -> Gen b -> Gen (a, b)
-- generalPair :: (Seed -> (a, Seed)) -> (Seed -> (b, Seed)) -> Seed -> ((a,b), Seed)
generalPair gena genb seed = ((a, b), seed'')
  where ra     = gena seed
        a      = fst ra
        seed'  = snd ra
        rb     = genb seed'
        b      = fst rb
        seed'' = snd rb

randPair' = generalPair randLetter rand

Generalizing lists of generators

repRandom :: [Gen a] -> Gen [a]
repRandom [] seed = ([], seed)  -- not sure about this equation
repRandom (g:gs) seed = (a : fst (repRandom gs seed'), seed')
  where ra = g seed
        a = fst ra
        seed' = snd ra

Threading the random number state

genTwo :: Gen a -> (a -> Gen b) -> Gen b
genTwo gen f seed = f a seed
  where ra    = gen seed
        a     = fst ra
        seed' = snd ra

mkGen :: a -> Gen a
-- mkGen :: a -> Seed -> (a, Seed)
mkGen a seed = (a, seed)

Date: 2016-02-13 Sat 00:00

Author: Stefano Rodighiero

Created: 2019-10-05 Sat 18:10

Validate