how can I shuffle a list using a monad? - haskell

I have these data types:
data Card = Card One | Card Two | ...
data Deck = Deck [Card]
I want to shuffle the list cards in a deck
Here's where I am so far:
import System.Random
shuffle :: Deck -> Deck
shuffle (Deck c) = Deck $ shuffleOnce randomN c
where randomN = randomIO >>= (\x -> return (x `mod` 52))
shuffleOnce n c = (findNth n c : deleteNth n c)
findNth 0 (x:_) = x
findNth n (_:xs) = findNth (n-1) xs
deleteNth 0 (_:xs) = xs
deleteNth n (x:xs) = x : (deleteNth (n-1) xs)
The problem (obviously) is here:
where randomN = randomIO >>= (\x -> return (x `mod` 52))
I have no idea how to use the IO monad to generate a random number. Once I wrap the random number from ` to 52 in the monadic context, how do I get it out?
Or better question, how do I actually shuffle the deck?
Please help. full code here

Compiler said, that the problem is in (randomN 1). randomN function is called with an argument, but it doesn't accept one.
Then it couldn't figure out, which type of random number is expected, so we need to provide one here:
randomIO >>= (\x -> return (x `mod` 52)) :: IO Int
Then No instance for (Eq (IO Int)) error is received, which actually means, that Int is expected, but IO Int is provided. For convenience, we can swap the arguments of shuffleOnce n c and call this function as:
shuffleOnce c <$> randomN
After that, the error says:
Expected type: [Card]
Actual type: IO [Card]
We need to use <$> again instead of $, just to get "inside" IO:
Deck <$> shuffleOnce c <$> randomN
As a result, we receive:
Couldn't match expected type ‘Deck’ with actual type ‘IO Deck’
We can't do anything about it, but change the type of shuffle function:
shuffle :: Deck -> IO Deck
shuffle (Deck c) = Deck <$> shuffleOnce c <$> randomN
where randomN = randomIO >>= (\x -> return (x `mod` 52)) :: IO Int
shuffleOnce c n = (findNth n c : deleteNth n c)
findNth 0 (x:_) = x
findNth n (_:xs) = findNth (n-1) xs
deleteNth 0 (_:xs) = xs
deleteNth n (x:xs) = x : (deleteNth (n-1) xs)
Then we realise, that shuffle is a function with side effects, because it contains random numbers, which are "received from outside world". And actually having this information in the type definition seems logical.

Related

How do I deal with the error "No instance for (Control.Monad.IO.Class.MonadIO [])"?

Good evening everybody! This is a question concerning Haskell. I want to get x random elements from a list by using a function.
The problem I get is that when I try to use random numbers, with randomRIO. I get the error message:
No instance for (Control.Monad.IO.Class.MonadIO [])
arising from a use of `randomRIO'
This error message suddenly goes away when i use print or return. But I dont want to use print, and return messes up the output to a nested list [[a]] instead of [a].
Does any of you have a tip on what I can do to extract x random elements from the list, in the form of a list?
The type would be something like this. xRanElems :: [a] -> Int -> [a]
where the second Int is an accumulator but I got that covered.
xRanElems xs n = do
r <- randomRIO (0, n)
xRanElems2 xs n r
where n is just length xs - 1
xRanElems2 xs n r = (xs !! r) : (xRanElems (xsWithOutSelectedElem xs r) (n-1))
Thankful for any input!
The following typechecks:
import System.Random
xRanElems :: [a] -> Int -> IO [a]
xRanElems xs n = do
-- randomRIO :: Random t
-- => (t, t) -> IO t
r <- randomRIO (0, n) -- r :: Int
xRanElems2 xs n r
xRanElems2 :: [a] -> Int -> Int -> IO [a]
xRanElems2 xs n r =
let (a,b:c) = splitAt r xs
in
fmap (b :) -- [a] -> [a]
(xRanElems -- IO [a] -> IO [a]
(a++c) (n-1))
Trying to run it, e.g. with xRanElems [1..3] 2, reveals that it loops forever.
This is because you need to provide the base case in xRanElems to stop the recursion, e.g. returning [] when n <= 0.
The above code also contains an off-by-1 error which you're invited to fix.

How do you parallelize lazily read information from stdin in Haskell?

I'm working with this code I wrote, and for some reason threadscope keeps telling me that it's almost never using more than one core at a time. I think the problem is that in order to get the second line it needs to fully evaluate the first line, but I can't figure out an easy way to get it to read in 11 lines at a time.
module Main where
import Control.Parallel
import Control.Parallel.Strategies
import System.IO
import Data.List.Split
import Control.DeepSeq
process :: [String] -> [String]
process lines = do
let xs = map (\x -> read x :: Double) lines
ys = map (\x -> 1.0 / (1.0 + (exp (-x)))) xs
retlines = map (\x -> (show x ) ++ "\n") ys
retlines
main :: IO ()
main = do
c <- getContents
let xs = lines c
ys = (process xs) `using` parBuffer 11 rdeepseq
putStr (foldr (++) [] ys)
If I am reading this code right, parBuffer n only sparks the first n elements -- all the rest are evaluated in the usual Haskell way.
parBuffer :: Int -> Strategy a -> Strategy [a]
parBuffer n strat = parBufferWHNF n . map (withStrategy strat)
parBufferWHNF :: Int -> Strategy [a]
parBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0))
where -- ret :: [a] -> [a] -> [a]
ret (x:xs) (y:ys) = y `par` (x : ret xs ys)
ret xs _ = xs
-- start :: Int -> [a] -> [a]
start 0 ys = ys
start !_n [] = []
start !n (y:ys) = y `par` start (n-1) ys
Note in particular that start 0 ys = ys and not, say, start 0 ys = evaluateThePreviousChunk `pseq` start n0 ys or something that would start up more sparks. The documentation definitely doesn't make this clear -- I don't think "rolling buffer strategy" obviously implies this behavior, and I agree it's a bit surprising, to the point that I wonder whether this is just a bug in the parallel library that nobody caught yet.
You probably want parListChunk instead.

Is there any terminating fold in Haskell?

I need some kind of fold which can terminate if I already have the data I want.
For example I need to find first 3 numbers which are greater than 5. I decided to use Either for termination and my code looks like this:
terminatingFold :: ([b] -> a -> Either [b] [b]) -> [a] -> [b]
terminatingFold f l = reverse $ either id id $ fold [] l
where fold acc [] = Right acc
fold acc (x:xs) = f acc x >>= flip fold xs
first3NumsGreater5 acc x =
if length acc >= 3
then Left acc
else Right (if x > 5 then (x : acc) else acc)
Are there some more clever/generic approaches?
The result of your function is a list, and it would be desirable if it were produced lazily, that is, extracting one item from the result should only require evaluating the input list up until the item is found there.
Unfolds are under-appreciated for these kinds of tasks. Instead of focusing on "consuming" the input list, let's think of it as a seed from which (paired with some internal accumulator) we can produce the result, element by element.
Let's define a Seed type that contains a generic accumulator paired with the as-yet unconsumed parts of the input:
{-# LANGUAGE NamedFieldPuns #-}
import Data.List (unfoldr)
data Seed acc input = Seed {acc :: acc, pending :: [input]}
Now let's reformulate first3NumsGreater5 as a function that either produces the next output element from the Seed, of signals that there aren't any more elements:
type Counter = Int
first3NumsGreater5 :: Seed Counter Int -> Maybe (Int, Seed Counter Int)
first3NumsGreater5 (Seed {acc, pending})
| acc >= 3 =
Nothing
| otherwise =
case dropWhile (<= 5) pending of
[] -> Nothing
x : xs -> Just (x, Seed {acc = succ acc, pending = xs})
Now our main function can be written in terms of unfoldr:
unfoldFromList ::
(Seed acc input -> Maybe (output, Seed acc input)) ->
acc ->
[input] ->
[output]
unfoldFromList next acc pending = unfoldr next (Seed {acc, pending})
Putting it to work:
main :: IO ()
main = print $ unfoldFromList first3NumsGreater5 0 [0, 6, 2, 7, 9, 10, 11]
-- [6,7,9]
Normally an early termination-capable fold is foldr with the combining function which is non-strict in its second argument. But, its information flow is right-to-left (if any), while you want it left-to-right.
A possible solution is to make foldr function as a left fold, which can then be made to stop early:
foldlWhile :: Foldable t
=> (a -> Bool) -> (r -> a -> r) -> r
-> t a -> r
foldlWhile t f a xs = foldr cons (\acc -> acc) xs a
where
cons x r acc | t x = r (f acc x)
| otherwise = acc
You will need to tweak this for t to test the acc instead of x, to fit your purposes.
This function is foldlWhile from https://wiki.haskell.org/Foldl_as_foldr_alternative, re-written a little. foldl'Breaking from there might fit the bill a bit better.
foldr with the lazy reducer function can express corecursion perfectly fine just like unfoldr does.
And your code is already lazy: terminatingFold (\acc x -> Left acc) [1..] => []. That's why I'm not sure if this answer is "more clever", as you've requested.
edit: following a comment by #danidiaz, to make it properly lazy you'd have to code it as e.g.
first3above5 :: (Foldable t, Ord a, Num a)
=> t a -> [a]
first3above5 xs = foldr cons (const []) xs 0
where
cons x r i | x > 5 = if i==2 then [x]
else x : r (i+1)
| otherwise = r i
This can be generalized further by abstracting the test and the count.
Of course it's just reimplementing take 3 . filter (> 5), but shows how to do it in general with foldr.

Haskell recursion with random numbers and IO

For the 99 Haskell questions, specifically the 23rd one, I need to
"Extract a given number of randomly selected elements from a list.
Example (in lisp):
(rnd-select '(a b c d e f g h) 3)
(E D A)
"
Which I have implemented like so:
import System.Random
import Control.Monad
removeAt :: [a] -> Int -> [a]
removeAt (x:xs) i
| i > 0 = x : removeAt xs (i-1)
| otherwise = xs
rndSelect :: (RandomGen g) => [a] -> Int -> g -> IO [a]
rndSelect _ 0 _ = return []
rndSelect xs n gen = do
let (pos, newGen) = randomR (0, length xs - 1) gen
rest <- rndSelect (removeAt xs pos) (n-1) newGen
return $ (xs!!pos):rest
-- for an explanation of what this is doing see EXPLANATION below
As far as I can tell this works, but what I'm concerned about are those last two lines. I'm new to this and I don't know the associated costs of the '<-' operator is or bouncing in and out of IO repeatedly like I'm doing. Is this efficient, is there a better way to do this that doesn't involve bouncing IO, or is there no real overheads involved?
Any insight you have is appreciated, since I've only recently started learning these more sophisticated concepts in Haskell and haven't yet gotten used to reasoning about Haskell's IO system.
EXPLANATION: In order to do this I've decided that I should randomly select one element from the list using the randomR function (returns a random number in a given range), and keep doing this recursively until I've taken n elements.
I've made a couple assumptions about the problem that have lead me to this approach. Firstly I've assumed that rndSelect can select a specific element from the list only once, and secondly I've assumed that each element should have an equal probability of being picked.
PS: it's my first question on SO so if I've formatted the question poorly feel free to tell me.
You do not need IO for this, since randomR does not require it. What you need to do however, is to thread the random number generator through your computation:
import System.Random
import Control.Monad
removeAt :: [a] -> Int -> [a]
removeAt (x:xs) i
| i > 0 = x : removeAt xs (i-1)
| otherwise = xs
rndSelect :: (RandomGen t, Num a) => [a1] -> a -> t -> ([a1], t)
rndSelect _ 0 g = ([],g)
rndSelect xs n gen =
let (pos, newGen) = randomR (0, length xs - 1) gen
(rest,ng) = rndSelect (removeAt xs pos) (n-1) newGen
in ((xs!!pos):rest, ng)
If you're concerned about overheads going from IO to pure code, don't be. Instead you can try mwc-random package which will be atleast an order of magnitude faster in this case. Further, you could get additional benefit using any random access data structure instead of list if you have many elements.
You can avoid IO as :
rndSelect :: (RandomGen g) => [a] -> Int -> g -> [a]
rndSelect _ 0 _ = return []
rndSelect xs n gen = do
let (pos, newGen) = randomR (0, length xs - 1) gen
rest = rndSelect (removeAt xs pos) (n-1) newGen
in (xs!!pos):rest

Improving code to generate a distribution

I am new to Haskell and I wonder how/if I can make this code more efficient and tidy. It seems unnecessarily long and untidy.
My script generates a list of 10 averages of 10 coin flips.
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
Any help would be appreciated, thanks.
I'd tackle this problem in a slightly different way. First I'd define a function that would give me an infinite sampling of flips from a Bernoulli distribution with success probability p:
flips :: Double -> StdGen -> [Bool]
flips p = map (< p) . randoms
Then I'd write distBernoulli as follows:
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
I think this matches your definition of 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]
(Note that I'm using splitEvery from the handy split package, so you'd have to install the package and add import Data.List.Split (splitEvery) to your imports.)
This approach is slightly more general, and I think a little neater, but really the main difference is just that I'm using randoms and splitEvery.
EDIT: I posted this too fast and didn't match behavior, it should be good now.
import Control.Monad.Random
import Control.Monad (liftM, replicateM)
KNOWLEDGE: If you like randoms then use MonadRandom - it rocks.
STYLE: Only importing symbols you use helps readability and sometimes maintainability.
output :: IO [Double]
output = liftM (map dist) getLists
Note: I've given output an explicit type, but know it doesn't have to be IO.
STYLE:
1) Its usually good to separate your IO from pure functions. Here I've divided out the getting of random lists from the calculation of distributions. In your case it was pure but you combined getting "random" lists via a generator with the distribution function; I would divide those parts up.
2) Read Do notation considered harmful. Consider using >>= instead of
output = do
gen <- new
return $ dist gen
you can do:
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)
KNOWLEDGE In Control.Monad anything ending in an M is like the original but for monads. In this case, replicateM should be familiar if you used the Data.List replicate function.
getList :: MonadRandom m => Int -> m [Int]
getList m = liftM (map (subtract 1 . (*2)) . take m) (getRandomRs (0,1::Int))
STYLE: If I do something lots of times I like to have a single instance in its own function (getList) then the repetition in a separate function.
I'm not sure I understand your code or your question...
But it seems to me all you'd need to do is generate a list of random ones and zeroes, and then divide each of them by their length with a map and add them together with a foldl.
Something like:
makeList n lis = if n /= 0 then
makeList (n-1) randomR(0,1) : lis
else
lis
And then make it apply a Map and Foldl or Foldr to it.
Using the above, I am now using this.
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
Thanks for your help, and I welcome any further comments :)

Resources