First of all, my apologies for the non-descriptive title. Since I have no idea what's actually going on I can't really make it any more specific.
Now for my question. I have implemented the following snippet for problem 23 of the 99 Haskell problems, which should randomly select n
items from a list:
rndSelect' :: RandomGen g => [a] -> Int -> g -> ([a], g)
rndSelect' _ 0 gen = ([], gen)
rndSelect' [] _ _ = error "Number of items requested is larger than list"
rndSelect' xs n gen = ((xs !! i) : rest, gen'')
where (i, gen') = randomR (0, length xs - 1) gen
(rest, gen'') = (rndSelect' (removeAt xs i) (n - 1) gen')
rndSelectIO' :: [a] -> Int -> IO [a]
rndSelectIO' xs n = getStdRandom $ rndSelect' xs n
removeAt :: [a] -> Int -> [a]
removeAt xs n
| length xs <= n || n < 0 = error "Index out of bounds"
| otherwise = let (ys, zs) = splitAt n xs
in ys ++ (tail zs)
Now when I load this in ghci
this works correctly for valid arguments:
*Main> rndSelectIO' "asdf" 2 >>= putStrLn
af
However, strange things happen when I use an index that is out of bounds:
*Main> rndSelectIO' "asdf" 5 >>= putStrLn
dfas*** Exception: Number of items requested is larger than list
*Main> rndSelectIO' "asdf" 2 >>= putStrLn
*** Exception: Number of items requested is larger than list
As you can see, the following 2 (for me) unexpected things happen:
- Instead of directly giving an error it first print a permutation of the input.
- After it has given an error once, it won't execute at all anymore.
I suspect that 1. has to do with lazy evaluation, but I have absolutely no clue why 2. happens. What's going on here?
The
getStdRandom
function basically looks up aStdGen
value in a global variable, runs some function on it, puts the new seed back into the global variable, and returns the result to the caller.If the function in question returns with an error, that error gets put into the global variable. Now all attempts to use this global variable will throw an exception. (I told you global variables are evil!
;-)
)Try calling
getStdGen
manually yourself. It will either print out the current random seed, or throw an exception. If it throws an exception... there's your problem.I believe you can use
setStdGen
to reset the thing.