खोज…


परिचय

राज्य के भिक्षु एक प्रकार के मोनाड होते हैं जो एक राज्य को ले जाते हैं जो कि मोनाड में चलने वाले प्रत्येक अभिकलन के दौरान बदल सकता है। क्रियान्वयन प्रपत्र वाले होते हैं, 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)


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow