Home > Software engineering >  Calculationg perplexity (in natural language processing) manually
Calculationg perplexity (in natural language processing) manually

Time:11-23

I wrote the code below, and now want to rewrite it using a Monad. And with do notation. I tried the do notation below but gets the output. It seems to have something to do with the tuples that I use binding for, which works in the original code:

seed::Int
seed = 2

giveList :: [Int]
giveList = [8,9,4,5,2]

generator = mkStdGen seed

giveRandomElement :: [a] -> a
giveRandomElement lst = lst !! rand where
  n = length lst
  (rand, _) = randomR (0,(n-1)) generator

random_response :: R.StdGen -> a -> [a] -> (a, R.StdGen)
random_response g true_answer answers =
  let (val, g') = R.random g
      (val', g'') = R.random g' :: (Bool, R.StdGen)
  in if val then (true_answer, g'') 
     else (giveRandomElement answers , g'')

random_response_monad :: R.StdGen -> a -> [a] -> (a, R.StdGen)
random_response_monad g true_answer answers =  do 
     (val, g') <- R.random g :: (Bool, R.StdGen)
     (val', g'') <- R.random g' :: (Bool, R.StdGen)
     if val then return (true_answer, g'')
     else return (giveRandomElement answers, g'')

OUTPUT:
error:
* Couldn't match expected type `StdGen'
              with actual type `(Bool, StdGen)'
* In the pattern: (val, g')
  In a stmt of a 'do' block: (val, g') <- random g :: (Bool, StdGen)
  In the expression:
    do (val, g') <- random g :: (Bool, StdGen)
       (val', g'') <- random g' :: (Bool, StdGen)
       if val then
           return (true_answer, g'')
       else
           return (giveRandomElement answers, g'')
(val, g') <- R.random g :: (Bool, R.StdGen)

I suspect that the type error I get is due to the <- rather than the let-binding. Is this correct, and where is my conceptual misunderstanding, and how can I fix the code?

Furthermore is it correct that what I am trying to do is rewriting it using a state-monad?

EDIT: Tried to answer below exercise suggestion

I have tried the below but get a parser error.

random_response_monad :: a -> [a] -> State R.StdGen a
random_response_monad true_answer answers =  do 
    let random' = state $ randomR(0,1)
    val <- random' 
    val' <- random'
    if val then giveRandomElement answers 
    else giveRandomElement answers

Note that I changed both branches of if-statement to a call of same function and argument for testing.

CodePudding user response:

I think what you actually want by “rewrite it using a Monad” is indeed rewriting it in the state monad. It's no good using monad syntax if you don't have an actual monad type.

(It so happens that plain tuples can also act as a monad, but better ignore that, it's more confusing than helpful.)

The point of expressing specifically randomized calculations in a state monad is that it avoids the awkward and error-prone carrying around of explicit new versions of the random generator. I.e., you want to replace

  let (val, g') = R.random g
      (val', g'') = R.random g'

with simply

  val <- random'
  val' <- random'

To get there, let's first re-order the signature:

random_response :: a -> [a] -> (R.StdGen -> (a, R.StdGen))

The part in parentheses is basically State R.StdGen a. In fact you can rewrite it quite easily to have the signature

random_response :: a -> [a] -> State R.StdGen a

I'll leave that as an exercise, but will address giveRandomElement instead, which currently is wrong. Note that generator is just a global constant, i.e. giveRandomElement will always yield the same element of the list. That's no good.

Instead, this should also have a proper random type, preferrably again the state monad. You can write it thus:

giveRandomElement :: [a] -> State R.StdGen a
giveRandomElement lst = do
  let n = length lst
  rand <- state $ randomR (0, n-1)
  return $ lst !! rand
  • Related