2016-06-10 6 views
6

मैं एक 2 डी विमान पर किसी ऑब्जेक्ट के आंदोलन का पालन करने की कोशिश कर रहा हूं, जिसे "आगे, बाएं या दाएं" आदेशों की सूची दी गई है।2 आयामों में ट्रैकिंग आंदोलन में हास्केल राज्य मोनड

अब तक मेरे पास ऐसे कार्य हैं जो वस्तु (स्थिति, स्थिति और चाल) के घटकों में लेते हैं और सभी चाल पूरी होने के बाद अंतिम स्थिति लौटाते हैं या सभी पदों को पारित किया जाता है।

वस्तु का राज्य प्रपत्र Sstate (Dir dx dy) (Pos px py) [m] में है और एक कदम रिकर्सिवली चाल की सूची के सिर को लागू करने के नए राज्यों

यानी Sstate (Dir 1 0) (Pos 0 0) "fff" -> Sstate (Dir 1 0) (Pos 0 1) "ff"

type Mov = Char 

data Pos = Pos Int Int 
deriving (Show, Eq) 

data Dir = Dir Int Int 
deriving (Show, Eq) 

data Sstate = Sstate Dir Pos [Mov] 
deriving (Show, Eq) 

move :: Sstate -> Sstate 
move (Sstate (Dir dx dy) (Pos px py) [] ) = Sstate (Dir dx dy) (Pos px py) [] 
move (Sstate (Dir dx dy) (Pos px py) (m:ms)) 
| m == 'f' = ss dx dy (dx + px) (dy + py) ms 
| m == 'l' = ss (-dy) dx px  py  ms 
| m == 'r' = ss dy (-dx) px  py  ms 
| otherwise = ss dy dx px  py  [] 
where 
    ss a b c d = Sstate (Dir a b) (Pos c d) 

end :: Dir -> Pos -> [Mov] -> Sstate 
end d p ms = iterate move initialState !! n 
where 
    initialState = Sstate d p ms 
    n = length ms + 1 

position :: Sstate -> Pos 
position (Sstate _ p _) = p 

route :: Dir -> Pos -> [Mov] -> [Pos] 
route d p ms = map position . take n . iterate move $ initialState 
where 
    initialState = Sstate d p ms 
    n = length ms + 1 

उत्पन्न करने के लिए दे रही है के होते हैं

λ: let x = Sstate (Dir 0 1) (Pos 0 2) "ff" 

λ: move x 
Sstate (Dir 0 1) (Pos 0 3) "f" 

λ: end (Dir 0 1) (Pos 0 2) "ff" 
Sstate (Dir 0 1) (Pos 0 4) "" 

λ: route (Dir 0 1) (Pos 0 2) "ff" 
[Pos 0 2,Pos 0 3,Pos 0 4] 

ऐसा लगता है लेकिन यह ऐसा लगता है कि यह कुछ ऐसा है जो State monad के लिए पूछ रहा है। मुझे State monad भ्रमित लगता है लेकिन मुझे लगता है कि यह मेरी समझ में मदद करेगा अगर कोई यह दिखाने के लिए पर्याप्त दयालु होगा कि इसका उपयोग कैसे किया जा सकता है।

+0

पढ़ें [इस ट्यूटोरियल] (http://learnyouahaskell.com/ कुछ राज्यों के मोनैड समेत कुछ मोनैड के बारे में)। – Bakuriu

+0

जो मुझे राज्य मोनैड के साथ भ्रमित पाया गया वह यह है कि राज्य वास्तव में मोनड नहीं है। एक xmonad क्या है जो एक राज्य को संशोधित करता है और एक मान वापस करता है: s -> (ए, एस) – mb14

+0

@ mb14 यह समझने का एक तरीका यह है कि राज्य एक अतिरिक्त पैरामीटर और अतिरिक्त रिटर्न मान दोनों है; प्रकार 'ए -> बी' का एक प्रकार 'ए -> एस -> (बी, एस) प्रकार का एक कार्य बन जाता है। करी से आपको इस प्रकार 'ए' टाइप करने और एक नया फ़ंक्शन लौटने के बारे में सोचने की सुविधा मिलती है, जब एक राज्य दिया जाता है, तो' बी 'प्रकार और एक नया राज्य ('a -> (s -> (बी, एस))। 'मोनैड,' >> = 'ऑपरेटर के माध्यम से, बस आपको इस तरह के कार्यों को एक साथ करने देता है। आखिरकार, आप प्रकार के कार्य के साथ हवाएं -> (टी, एस) ' जो प्रारंभिक स्थिति को 't' प्रकार के मान में बदल देता है। – chepner

उत्तर

1

यहां कुछ सरल 'स्टार्टर' कोड है जो आपके मॉड्यूल को राज्य के संदर्भ में कुछ सुधारों के साथ विस्तारित करता है। आपको उनके साथ झुकाव के दौरान LYAH अध्याय जैसे ट्यूटोरियल को देखने की आवश्यकता होगी, मुझे लगता है। मैं हस्ताक्षर छोड़ देता हूं, जो तेजी से जटिल हो जाते हैं, लेकिन ghci में प्रकार पूछना निर्देशक होगा। तुम्हें पता है, हम लागू runState

import Control.Monad.State 
import Control.Monad.Writer -- for the position-remembering example 

उसके बाद निम्न move

step = do      -- step the state once with your `move`, 
sstate <- get     -- whatever the state is 
put (move sstate) 
-- this little program could also be written: `modify move` which shows the 
-- link between what you wrote and State a little more clearly 

steps = do      -- repeatedly apply `step` to the state 
    Sstate _ _ ls <- get   -- til there are no moves, then stop 
    if null ls 
    then return()  -- could be: unless (null ls) $ do step ; steps 
    else step >> steps 

stepsIO = do      -- do all steps as with `steps`, but print 
    [email protected](Sstate a b ls) <- get -- the current state at each state update 
    liftIO $ print sstate 
    if null ls then liftIO (putStrLn "Done!") 
      else step >> stepsIO 

stepsPrintPosition = do   -- do all steps as with `steps`, printing 
    Sstate _ b ls <- get   -- only position at each state update 
    liftIO $ do putStr "current position: " 
       print b 
    if null ls then liftIO (putStrLn "Done!") 
      else do step 
        stepsPrintPosition 

stepsAccumulatePositions = do -- move through all states as with `steps` 
    [email protected](Sstate a b ls) <- get -- but use `tell` to keep adding the current 
    tell [b]      -- position to the underlying list 
    if null ls then return()  -- of positions 
      else step >> stepsAccumulatePositions 

example = Sstate (Dir 0 1) (Pos 0 2) "ffff" 

की अपनी परिभाषा का उपयोग करते हुए सभी काम step, steps, stepsIO आदि जैसी चीजों का उपयोग करने के चाहिए जोड़ने की जरूरत है; यह हमें एक नए राज्य

runStateT :: StateT s m a -> s -> m (a, s) 

करने के लिए एक राज्य से एक समारोह देता है निश्चित रूप से यह सिर्फ newtype परिभाषा

newtype StateT s m a = StateT {runStateT :: s -> m (a, s)} 

रैपिंग हमें परमिट फैंसी s -> m (a, s) बातें लिखने के लिए एक्सेसर है, सरल s -> m (a, s) का उपयोग कर बिट्स, लेकिन न्यूटाइप हुड के तहत, यह हमेशा एक फंक्शन s -> m (a, s) हम डॉटेशन में लिख रहे हैं।

बेशक, एक बार जब हम runStateT से अनचाहे करते हैं और हमारे कार्य s -> m (a, s) हैं, तो हमें इसे प्रारंभिक स्थिति के साथ आपूर्ति करने की आवश्यकता है। यह कैसे इस GHCi

>>> example 
Sstate (Dir 0 1) (Pos 0 2) "ffff" 

>>> runStateT step example   -- we step the state once with move 
((),Sstate (Dir 0 1) (Pos 0 3) "fff") 

>>> runStateT steps example   -- we keep stepping till there are no moves 
((),Sstate (Dir 0 1) (Pos 0 6) "") 

>>> runStateT stepsIO example   -- we print state at each state update 
Sstate (Dir 0 1) (Pos 0 2) "ffff" 
Sstate (Dir 0 1) (Pos 0 3) "fff" 
Sstate (Dir 0 1) (Pos 0 4) "ff" 
Sstate (Dir 0 1) (Pos 0 5) "f" 
Sstate (Dir 0 1) (Pos 0 6) "" 
Done! 
((),Sstate (Dir 0 1) (Pos 0 6) "") 

>>> runStateT stepsPrintPosition example -- print position only at state updates 
current position: Pos 0 2 
current position: Pos 0 3 
current position: Pos 0 4 
current position: Pos 0 5 
current position: Pos 0 6 
Done! 
((),Sstate (Dir 0 1) (Pos 0 6) "") 


-- the WriterT examples accumulate a 'monoid' of things you keep 
-- adding to with `tell xyz` Here we accumulate a [Position] 
-- execXYZ and evalXYZ, where they exist, return less information than runXYZ 

>>> runWriterT $ runStateT stepsAccumulatePositions example 
(((),Sstate (Dir 0 1) (Pos 0 6) ""),[Pos 0 2,Pos 0 3,Pos 0 4,Pos 0 5,Pos 0 6]) 

>>> execWriterT $ evalStateT stepsAccumulatePositions example 
[Pos 0 2,Pos 0 3,Pos 0 4,Pos 0 5,Pos 0 6] 

में परीक्षण के द्वारा काम करता है कोड में ऊपर मैं mtl typeclasses उपयोग कर रहा हूँ और उसके बाद runStateT और runWriterT का उपयोग कर "व्याख्या" या हस्ताक्षर से जुड़े वर्ग विशेषज्ञ को देखने के लिए सबसे आसान है। ये कंक्रीट प्रकार StateT और WriterTControl.Monad.Trans.{State/Writer} में परिभाषित हैं, जो कक्षाओं को छोड़ सकते हैं, और केवल उन ठोस प्रकारों के साथ सीधे लिख सकते हैं, उन मॉड्यूल को आयात कर सकते हैं। एकमात्र अंतर यह होगा कि आपको एक मामले में lift $ tell [b] करने की आवश्यकता है जहां मैं दो प्रभाव, राज्य और लेखन या जिसे आप इसे कॉल करना चाहते हैं।

राज्य के विश्लेषण के बारे में बहुत कुछ कहना है जिसके साथ आप काम कर रहे हैं लेकिन यह उभरा होगा कि आप इसे कैसे पुन: कार्य कर सकते हैं, अगर आप उपर्युक्त सोचते हैं।

2

ध्यान दें कि आप सीधे यहाँ State इकाई का उपयोग करने, end और route रूप foldl' और scanl' का उपयोग कर लिखा जा सकता है एक बार आप कुछ है कि एक राज्य पर संचालित होता है, बल्कि हिस्सा होने की तुलना में के रूप में एक Mov पर विचार की जरूरत नहीं है राज्य केSstate के लिए रिकॉर्ड सिंटैक्स भी मदद करता है।

type Mov = Char 
data Pos = Pos Int Int deriving (Show, Eq) 
data Dir = Dir Int Int deriving (Show, Eq) 
data Sstate = Sstate {direction :: Dir, position :: Pos} deriving (Show, Eq) 

-- A helper to simplify move 
changeDir :: Mov -> Dir -> Dir 
changeDir 'l' (Dir x y) = Dir (-y) x 
changeDir 'r' (Dir x y) = Dir y (-x) 
changeDir m (Dir x y) = Dir y x 

move :: Mov -> Sstate -> Sstate 
move 'f' [email protected](Sstate (Dir dx dy) (Pos px py)) = oldState { position = Pos (dx + px) (dy + py) } 
move m [email protected](Sstate dir _) = oldState { direction = changeDir m dir } 

end :: [Mov] -> Sstate -> Sstate 
end ms initialState = foldl' (flip move) initialState ms 

route :: [Mov] -> Sstate -> [Pos] 
route ms initialState = map position $ scanl' (flip move) initialState ms 

फिर अपने उदाहरण बन:

λ: let x = Sstate {direction = Dir 0 1, position = Pos 0 2} 

λ: move 'f' x 
Sstate {direction = Dir 0 1, position = Pos 0 3} 

λ: end "ff" x 
Sstate {direction = Dir 0 1, position = Pos 0 4} 

λ: route "ff" x 
[Pos 0 2,Pos 0 3,Pos 0 4] 

मैं एक व्यायाम के रूप में छोड़ देंगे (या कोई अधिक जानकार और मुझे कम से कम आलसी करने के लिए)

move :: Mov -> Sstate -> Sstate 
end :: [Mov] -> Sstate -> Sstate 
route :: [Mov] -> Sstate -> [Pos] 

के लिए अनुकूल करने के लिए
move :: Mov -> State Sstate() 
-- or possibly move :: Mov -> State Sstate Sstate ? 
-- Like I said, more knowledgeable than me 
end :: [Mov] -> State Sstate Sstate 
route :: [Mov] -> State Sstate [Pos] 
+0

धन्यवाद। वास्तव में सहायक। कोड को किसी ऐसे व्यक्ति द्वारा रिफैक्टर किया जाता है जो जानता है कि वे क्या कर रहे हैं वास्तव में मेरी सीखने की प्रक्रिया में मदद करता है। – matthias

+0

विडंबना यह है कि अन्य लोगों के शुरुआती बिंदुओं को दोबारा सुधारना मैं अपनी अधिकांश शिक्षा कर रहा हूं। – chepner

+0

@chepner, '() 'मेरे लिए सबसे सरल और सबसे बेवकूफ दिखता है। – dfeuer

संबंधित मुद्दे