2017-04-08 103 views
-2

我正在玩状态monad和队列。目前,我有以下代码:退出状态Monad循环

{-# LANGUAGE ViewPatterns, FlexibleContexts #-} 
module Main where 

import Criterion.Main 
import Control.Monad.State.Lazy 
import Data.Maybe (fromJust) 
import Data.Sequence ((<|), ViewR ((:>))) 
import qualified Data.Sequence as S 

-------------------------------------------------------- 
data Queue a = Queue { enqueue :: [a], dequeue :: [a] } 
             deriving (Eq, Show) 
-- adds an item 
push :: a -> Queue a -> Queue a 
push a q = Queue (a:enqueue q) (dequeue q) 

pop :: Queue a -> Maybe (a, Queue a) 
pop q = if null (dequeue q) then 
      go $ Queue [] (reverse (enqueue q)) 
     else 
      go q 
    where go (Queue _ []) = Nothing 
     go (Queue en (x:de)) = Just (x, Queue en de) 

queueTst :: Int -> Queue Int -> Queue Int 
queueTst 0 q = q 
queueTst n q | even n = queueTst (n - 1) (push (100 + n) q) 
      | otherwise = queueTst (n - 1) 
          (if popped == Nothing then q 
          else snd (fromJust popped)) 
    where popped = pop q 
------------------------------------------------------------- 
pushS :: a -> S.Seq a -> S.Seq a 
pushS a s = a <| s 

pushS' :: a -> State (S.Seq a) (Maybe a) 
pushS' a = do 
    s <- get 
    put (a <| s) 
    return Nothing 

pushS'' :: a -> State (S.Seq a) (Maybe a) 
pushS'' a = get >>= (\g -> put (a <| g)) >> return Nothing 

popS :: S.Seq a -> Maybe (a, S.Seq a) 
popS (S.viewr -> S.EmptyR) = Nothing 
popS (S.viewr -> s:>r) = Just (r,s) 

popS' :: State (S.Seq a) (Maybe a) 
popS' = do 
    se <- get 
    let sl = popS'' se 
    put $ snd sl 
    return $ fst sl 
    where popS'' (S.viewr -> S.EmptyR) = (Nothing, S.empty) 
     popS'' (S.viewr -> beg:>r) = (Just r, beg) 

queueTstS :: Int -> S.Seq Int -> S.Seq Int 
queueTstS 0 s = s 
queueTstS n s | even n = queueTstS (n - 1) (pushS (100 + n) s) 
       | otherwise = queueTstS (n - 1) 
          (if popped == Nothing then s 
          else snd (fromJust popped)) 
     where popped = popS s 

queueTstST :: Int -> State (S.Seq Int) (Maybe Int) 
queueTstST n = 
    if (n > 0) then 
    if even n then 
     pushS' (100 + n) >> queueTstST (n - 1) 
    else 
     popS' >> queueTstST (n - 1) 
    else return Nothing 

main :: IO() 
main = defaultMain 
    [ bench "Twin Queue" $ whnf (queueTst 550) (Queue [500,499..1] []) 
    , bench "Sequence Queue" $ whnf (queueTstS 550) (S.fromList [500,499..1]) 
    , bench "State Queue" $ whnf 
        (runState (queueTstST 550)) (S.fromList [500,499..1]) 
    ] 

这是一些代码,但真的是与此有关的唯一功能是mainqueueTstST。有没有办法退出queueTstST,同时保留最后的“Maybe值”而不是“Nothing”?

回答

0
queueTstST :: Int -> State (S.Seq Int) (Maybe Int) 
queueTstST n = 
    if (n > 1) then 
    if even n then 
     pushS' (100 + n) >> queueTstST (n - 1) 
    else 
     popS' >> queueTstST (n - 1) 
    else popS' 
+0

这不是真正的正确答案。因为如果第二个循环是pushS',那么正确的答案就是'Nothing'。如果它是popS',那么它就是那个特定pops的'Just'值 - 不是随后的popS'。 – user1897830

+0

是的 - 你的权利。然而,我想知道是否有一种方法来退出循环与最后一个循环的值,即。如果你不知道天气,倒数第二圈甚至是赔率。 – user1897830

+0

但最初,第二个循环始终是popS',因为n = 1,在这里我通过中止递归来返回n = 1循环中的值。 – Gurkenglas

0

如果向递归函数添加参数,则可以记住上一个值。

queueTstST :: Int -> State (S.Seq Int) (Maybe Int) 
queueTstST n = go n Nothing 
    where 
    go :: Int -> Maybe Int -> State (S.Seq Int) (Maybe Int) 
    go n v = 
    if (n > 1) 
    then if even n 
     then pushS' (100 + n) >> go (n - 1) Nothing 
     else popS' >>= go (n - 1) 
    else return v