Haskell Language
राज्य मोनाड
खोज…
परिचय
State sa
जो एक गणना है कि वहन करती है और संभवतः के प्रकार के एक राज्य को संशोधित करता है का प्रतिनिधित्व करता है s
और प्रकार का एक परिणाम का उत्पादन a
है, लेकिन शब्द "राज्य इकाई" आम तौर पर किसी भी इकाई है जो एक राज्य में किया जाता है का उल्लेख कर सकते। mtl
और transformers
पैकेज राज्य के मठों के सामान्य कार्यान्वयन प्रदान करते हैं।
टिप्पणियों
हास्केल में आने वाले नए लोग अक्सर State
भिक्षुओं से दूर रहते हैं और इसे एक निषेध की तरह मानते हैं - जैसे कार्यात्मक प्रोग्रामिंग का दावा किया गया लाभ राज्य से बचने का है, इसलिए क्या आप State
उपयोग करते समय इसे नहीं खोते हैं? एक अधिक बारीक दृश्य यह है कि:
- राज्य छोटी, नियंत्रित खुराक में उपयोगी हो सकता है;
-
State
प्रकार खुराक को ठीक से नियंत्रित करने की क्षमता प्रदान करता है।
कारण है कि अगर आप action :: State sa
, यह आपको बताता है कि:
-
action
विशेष है क्योंकि यह एक राज्य पर निर्भर करता है; - राज्य में टाइप
s
, इसलिए आपके कार्यक्रम में किसी भी पुराने मूल्य सेaction
प्रभावित नहीं हो सकती है - केवल कुछs
या कुछs
से उपलब्ध मूल्य; -
runState :: State sa -> s -> (a, s)
स्टेटफुल एक्शन के चारों ओर एक "बैरियर" लगाता है, ताकि उस बैरियर के बाहर से इसकी प्रभावकारिता का अवलोकन न किया जा सके।
तो यह विशेष परिदृश्य में State
का उपयोग करने के लिए मानदंड का एक अच्छा सेट है। आप को देखने के लिए कि आपके कोड राज्य के दायरे को कम करने की है, दोनों के लिए एक संकीर्ण प्रकार चुनकर चाहते s
और डालकर runState
करने के लिए संभव के रूप में "नीचे" बंद के रूप में, (ताकि अपने कार्यों द्वारा के रूप में कुछ चीज के रूप में प्रभावित हो सकता है कि मुमकिन।
एक काउंटर के साथ एक पेड़ के नोड्स की संख्या
हमारे पास एक पेड़ डेटा प्रकार है:
data Tree a = Tree a [Tree a] deriving Show
और हम एक ऐसा कार्य लिखना चाहते हैं जो एक वृद्धिशील काउंटर से पेड़ के प्रत्येक नोड को एक संख्या प्रदान करता है:
tag :: Tree a -> Tree (a, Int)
लंबा रास्ता
पहले हम इसे चारों ओर से लंबा करेंगे, क्योंकि यह State
मोनाड के निम्न स्तर के यांत्रिकी को काफी अच्छी तरह से दिखाता है।
import Control.Monad.State
-- Function that numbers the nodes of a `Tree`.
tag :: Tree a -> Tree (a, Int)
tag tree =
-- tagStep is where the action happens. This just gets the ball
-- rolling, with `0` as the initial counter value.
evalState (tagStep tree) 0
-- This is one monadic "step" of the calculation. It assumes that
-- it has access to the current counter value implicitly.
tagStep :: Tree a -> State Int (Tree (a, Int))
tagStep (Tree a subtrees) = do
-- The `get :: State s s` action accesses the implicit state
-- parameter of the State monad. Here we bind that value to
-- the variable `counter`.
counter <- get
-- The `put :: s -> State s ()` sets the implicit state parameter
-- of the `State` monad. The next `get` that we execute will see
-- the value of `counter + 1` (assuming no other puts in between).
put (counter + 1)
-- Recurse into the subtrees. `mapM` is a utility function
-- for executing a monadic actions (like `tagStep`) on a list of
-- elements, and producing the list of results. Each execution of
-- `tagStep` will be executed with the counter value that resulted
-- from the previous list element's execution.
subtrees' <- mapM tagStep subtrees
return $ Tree (a, counter) subtrees'
पुनर्रचना
काउंटर को पोस्टइन्क्रिमेंट एक्शन में विभाजित करें
बिट हम कहाँ हैं get
टिंग वर्तमान काउंटर और फिर put
टिंग काउंटर + 1 को बंद विभाजित किया जा सकता है एक में postIncrement
कार्रवाई, कई सी-शैली भाषाओं क्या प्रदान करने के लिए इसी तरह की:
postIncrement :: Enum s => State s s
postIncrement = do
result <- get
modify succ
return result
एक उच्च-क्रम फ़ंक्शन में ट्री वॉक को विभाजित करें
ट्री वॉक लॉजिक को अपने फंक्शन में विभाजित किया जा सकता है, जैसे:
mapTreeM :: Monad m => (a -> m b) -> Tree a -> m (Tree b)
mapTreeM action (Tree a subtrees) = do
a' <- action a
subtrees' <- mapM (mapTreeM action) subtrees
return $ Tree a' subtrees'
इस और postIncrement
फ़ंक्शन के साथ हम tagStep
को फिर से tagStep
सकते हैं:
tagStep :: Tree a -> State Int (Tree (a, Int))
tagStep = mapTreeM step
where step :: a -> State Int (a, Int)
step a = do
counter <- postIncrement
return (a, counter)
Traversable
क्लास का इस्तेमाल करें
ऊपर दिए गए mapTreeM
समाधान को आसानी से Traversable
वर्ग के एक उदाहरण में फिर से लिखा जा सकता है:
instance Traversable Tree where
traverse action (Tree a subtrees) =
Tree <$> action a <*> traverse action subtrees
ध्यान दें कि यह उपयोग करने के लिए हमें आवश्यक Applicative
( <*>
के बजाय ऑपरेटर) Monad
।
उस के साथ, अब हम एक समर्थक की तरह tag
लिख सकते हैं:
tag :: Traversable t => t a -> t (a, Int)
tag init t = evalState (traverse step t) 0
where step a = do tag <- postIncrement
return (a, tag)
ध्यान दें कि यह किसी भी Traversable
प्रकार के लिए काम करता है, न कि केवल हमारे Tree
प्रकार के लिए!
Traversable
बॉयलरप्लेट से छुटकारा पा रहा है
GHC में एक DeriveTraversable
विस्तार होता है जो उपरोक्त उदाहरण लिखने की आवश्यकता को समाप्त करता है:
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
data Tree a = Tree a [Tree a]
deriving (Show, Functor, Foldable, Traversable)