从列表中提取随机元素(并将其删除),学习monad

时间:2016-01-08 18:26:13

标签: haskell random monad-transformers state-monad

我正在使用haskell wikibook和我自己的新手实验学习Haskell中的monads

作为我的第一步,我使用伟大的HaTeX图书馆(DanielDíaz)将与monad相关的书的部分复制到我自己的PDF中,同时轻轻阅读这些硬文(由于其内容很难)。

下面是我第一次尝试练习我学到的东西:7个版本的函数,它接受一个列表并从该列表中返回一个随机元素

现在我的主要问题是,我应该使用monad变形金刚来“删除元素”部分吗?我读过他们习惯混合monad,  我知道我将使用State monad和List monad

除此之外,对我的代码的任何评论都会感激不尽(风格最多,因为monad是关于写作风格的?:P)

import System.Random
import Control.Monad.Trans.State
import Control.Monad

------------------------ PRIMER INTENTO DE RANDOMNESS ------------------- -----

extractR' :: [a] -> a
extractR' xs = xs !! randomPos
  where
    n         = length xs
    randomPos = fst pareja
    pareja    = randomR (0,n-1) stdGen
    stdGen    = mkStdGen 0


-- extractR' siempre devuelve la misma cosa, al llamarla sobre la misma
-- lista   (por tanto NO SIRVE, NO ES ALEATORIO)
-- EJ:
-- *Deck> extractR' [1..100]
-- 46
-- (0.00 secs, 0 bytes)
-- *Deck> extractR' [1..100]
-- 46
-- (0.02 secs, 0 bytes)
-- *Deck> extractR' [1..100]
-- 46
-- (0.00 secs, 0 bytes)

------------------------ SEGUNDO INTENTO DE RANDOMNESS ------------------- -----

stdGen = mkStdGen 0


extractR'' :: StdGen -> [a] -> (a, StdGen)
extractR'' gen xs = (xs !! randPos , newGen)
  where 
    n = length xs
    (randPos, newGen) = randomR (0,n-1) gen


-- esta version permite arrastrar el RNGenerator, pero es incomodisima de usar:

-- *Deck> extractR'' stdGen [1..100]
-- (46,40014 40692)
-- (0.00 secs, 0 bytes)
-- *Deck> let x = snd (extractR'' stdGen [1..100]) in extractR'' x [1..100]
-- (56,1601120196 1655838864)
-- (0.00 secs, 0 bytes)

------------------------ TERCER INTENTO DE RANDOMNESS ------------------- ------

extractR''' :: [a] -> State StdGen a
extractR''' xs = state sol
  where
    n   = length xs
    sol = \s -> ( xs !! (randPos s) ,  newGen s)
    randPos st = fst $ randomR (0,n-1) st
    newGen st  = snd $ randomR (0,n-1) st

-- extractR''' xs = 
--   state ( \s -> 
--     ( xs !! (fst $ randomR (0, (length xs)-1) s ) 
--     , 
--     snd $ randomR (0, (length xs)-1) s ) )

-- pruebas en pantalla:

-- *Main> runState (extractR ['a'..'z']) (mkStdGen 0)
-- ('d',40014 40692)
-- (0.00 secs, 0 bytes)
-- *Main> runState (extractR ['a'..'z']) (mkStdGen 0)
-- ('d',40014 40692)
-- (0.00 secs, 0 bytes)
-- *Main> runState (extractR ['a'..'z']) (mkStdGen 7854)
-- ('n',314309970 40692)
-- (0.00 secs, 0 bytes)

-- works well, but the code is bad (non-monadic)
-- but we are in the State monad, so the following works:

extractR2 :: [a] -> State StdGen (a,a)
extractR2 xs = liftM2 (,) (extractR''' xs) (extractR''' xs)

------------------------ CUARTO INTENTO DE RANDOMNESS ------------------- ------

extractR'''' :: [a] -> State StdGen a
extractR'''' xs = pr1 >>= f
  where
    n   = length xs
    pr1 = state $ randomR (0,n-1)
    f   = \k -> state ( \s -> (xs !! k , s) )


-- monadic code, se actualiza 1 vez el StdGen

------------------------ QUINTO INTENTO DE RANDOMNESS ------------------- ------

extractR_ :: [a] -> State StdGen a
extractR_ xs = pr1 >>= f
  where
    n   = length xs
    pr1 = state $ randomR (0,n-1)
    f   = \k -> state ( \s -> (xs !! k , g s) )  
    g   = \stdG -> snd $ next stdG


-- monadic code, se actualiza 2 veces el StdGen

------------------------ SEXTO INTENTO DE RANDOMNESS ------------------- -------

extractR_' :: [a] -> State StdGen a
extractR_' xs = 
  do
    generator <- get              -- get = state $ \st -> (st,st)
    let n = length xs
    let (k, newGen) = randomR (0,n-1) generator
    put newGen
    return (xs!!k)


-- traduccion del codigo:
extractR_'' xs = 
  get >>= 
    \generator -> let n=length xs in 
      ( let (k, newGen)=randomR (0,n-1) generator in 
        (put newGen >> return (xs!!k) ) )


-- *Main> :t extractR_''
-- extractR_'' :: (RandomGen t, Monad m) => [b] -> StateT t m b
-- *Main> :t extractR_
-- extractR_ :: [a] -> State StdGen a



-- ======o     o======
--    ___________
--   |___________|    -------------------------------------------------------------------------------
--    |\  /\  /\|     ------------------------        VERSION FINAL          ------------------------
--    |_\/__\/__|     -------------------------------------------------------------------------------
--   |___________| 



 --    ??????????????????? (dont know which to choose)

顺便说一下,我这样做是为了完成我的数学学位,之后我会期待通过Haskell获得生活:D(我要求miracle?)

2 个答案:

答案 0 :(得分:1)

  

我应该使用monad变形金刚来“删除元素”部分吗?我读过它们用于混合monad,我知道我将使用State monad和List monad

但你没有使用列表monad!你只是使用列表。要说你使用列表monad意味着你使用(>>=)return专门用于列表(或类似Monad - 专用于列表的多态函数),你没有这样做。所以我认为使用State是完全合理的,而不是使用任何变换器。

  

除此之外,对我的代码的任何评论都会感激不尽(风格最多,因为monad是关于写作风格的?:P)

我最喜欢你的第四次尝试,这提醒的是这一次:

extractR'''' :: [a] -> State StdGen a
extractR'''' xs = pr1 >>= f
  where
    n   = length xs
    pr1 = state $ randomR (0,n-1)
    f   = \k -> state ( \s -> (xs !! k , s) )

我要做的唯一调整就是我发现你的f有点太复杂了。由于您根本没有修改状态,因此无需使用State特定功能;一个人可以写

    f   = \k -> return (xs !! k)

代替。然后可以应用monad法则,即:

m >>= (\x -> return (f x)) = fmap f m

我希望看到的最终实现是:

extractR :: [a] -> State StdGen a
extractR xs = fmap (xs!!) pr1
  where
    n   = length xs
    pr1 = state $ randomR (0,n-1)

(其中一个当然可以用其他几种方式拼写fmap,在这种情况下,主要区别在于美学,例如(<$>)liftAliftM。< / p>

答案 1 :(得分:0)

您可能需要查看MonadRandom包。

import Control.Monad.Random

extractR :: MonadRandom m => [a] -> m a
extractR xs = (xs !!) <$> getRandomR (0, length xs - 1)

MonadRandomIO满足RandT g m约束,它基本上是基于StateT StdGen的monad,可以在纯代码中使用。

此外,该库定义了uniform,它等同于您的extractR(虽然它的实现相当复杂)。