2010-07-30 11 views
10

मुझे अंततः मोनैड का उपयोग करने के तरीके पर पकड़ मिली (मुझे नहीं पता कि मैं उन्हें समझता हूं ...), लेकिन मेरा कोड कभी भी बहुत ही सुरुचिपूर्ण नहीं है। मुझे लगता है कि पकड़ के अभाव से Control.Monad पर उन सभी कार्यों में वास्तव में मदद मिल सकती है। तो मैंने सोचा था कि राज्य मोनड का उपयोग कर कोड के एक विशेष टुकड़े में इस पर युक्तियों के लिए पूछना अच्छा होगा।मोनैड के साथ अधिक सुरुचिपूर्ण कोड के लिए टिप्स?

कोड के लक्ष्य के यादृच्छिक क्षेत्रों के कई प्रकार की गणना करने के लिए है, और यह कुछ मैं कुछ और अधिक जटिल से पहले क्या करने की कोशिश कर रहा हूँ है। समस्या मैं एक ही समय में दो स्टेटफुल संगणना है कि है, और मुझे पता है कि उन्हें भव्यता के साथ रचना करने के लिए कैसे करना चाहते हैं:

  1. समारोह है कि यादृच्छिक संख्या जनरेटर अद्यतन करता है प्रकार के कुछ है Seed -> (DeltaPosition, Seed)
  2. समारोह है कि यादृच्छिक वॉकर की स्थिति को अद्यतन करता प्रकार DeltaPosition -> Position -> (Log, Position) (जहां Log बस मुझे रिपोर्ट करने के लिए यादृच्छिक वॉकर की वर्तमान स्थिति क्या है के लिए किसी तरह है) के बारे में कुछ है।

    composing :: (g -> (b, g)) -> (b -> s -> (v,s)) -> (s,g) -> (v, (s, g)) 
    composing generate update (st1, gen1) = let (rnd, gen2) = generate gen1 
                  (val, st2) = update rnd st1 
                 in (val, (st2, gen2)) 
    

    और फिर मैं इसे एक समारोह है कि राज्यों की रचना में बदल जाते हैं:

    मैं इस दो स्टेटफुल संगणना रचना के लिए एक समारोह है:

क्या मेरे द्वारा की गई यह है

stateComposed :: State g b -> (b -> State s v) -> State (s,g) v 
stateComposed rndmizer updater = let generate = runState rndmizer 
            update x = runState $ updater x 
           in State $ composing generate update 

और फिर मेरे पास सबसे सरल बात है, उदाहरण के लिए, एक यादृच्छिक वॉकर जो इसकी वर्तमान स्थिति में यादृच्छिक संख्या को जोड़ देगा:

update :: Double -> State Double Double 
update x = State (\y -> let z = x+y 
         in (z,z)) 

generate :: State StdGen Double 
generate = State random 

rolling1 = stateComposed generate update 

और एक समारोह को बार-बार ऐसा करने के लिए:

rollingN 1 = liftM (:[]) rolling1 
rollingN n = liftM2 (:) rolling1 rollings 
    where rollings = rollingN (n-1) 

और फिर, अगर मैं ghci में यह लोड और चलाएँ:

*Main> evalState (rollingN 5) (0,mkStdGen 0) 
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918] 

मैं जो मैं चाहता है, जो एक है मिल यादृच्छिक वॉकर द्वारा कब्जे की स्थिति की सूची। लेकिन ... मुझे लगता है कि ऐसा करने के लिए एक और शानदार तरीका होना चाहिए।

  1. मैं Control.Monad से चालाक कार्यों का उपयोग कर एक अधिक "monadic" रास्ते में उन कार्यों को फिर से लिखने कर सकते हैं,: मैं दो प्रश्न हैं?

  2. क्या इस तरह के राज्यों को जोड़ने के बारे में एक सामान्य पैटर्न है जिसका उपयोग किया जा सकता है? क्या इसका मोनैड ट्रांसफार्मर या ऐसा कुछ करने के साथ कुछ करना है?

+2

वैसे, यह एक अच्छा विचार ('monads-fd')' State' डेटा निर्माता का उपयोग कर, 'mtl' के उत्तराधिकारी में के बाद से बचने के लिए है,' राज्य 'को' स्टेटटी 'के संदर्भ में परिभाषित किया गया है और इसलिए' राज्य 'डेटा कन्स्ट्रक्टर मौजूद नहीं है। –

+0

@ ट्रेविसब्राउन वास्तव में, 'monads-fd' को' mtl' के पक्ष में बहिष्कृत किया गया है। (यह स्वीकार करते हुए कि आपकी टिप्पणी 5 साल पुरानी है।) – crockeea

उत्तर

11

अद्यतन: मैंने कहा जाना चाहिए था वहाँ वास्तव में यह करने के लिए एक बहुत अच्छा तरीका है कि State या बिल्कुल monads की आवश्यकता नहीं है कि:

takeStep :: (Double, StdGen) -> (Double, StdGen) 
takeStep (p, g) = let (d, g') = random g in (p + d, g') 

takeSteps n = take n . tail . map fst $ iterate takeStep (0, mkStdGen 0) 

यह वांछित के रूप में काम करता है:

*Main> takeSteps 5 
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918] 

आप दो अलग-अलग स्टेटफुल संगणना "रचना" के विचार करने के लिए प्रतिबद्ध नहीं कर रहे हैं, तो आप पूरा कर सकते हैं एक ही बात और अधिक सीधी:

*Main> takeSteps 5 
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918] 

यह दृष्टिकोण (बजाय एक State A रचना करने की कोशिश कर के एक एकल इकाई में सभी राज्य हेरफेर कर रहे हैं और:

takeStep :: State (Double, StdGen) Double 
takeStep = do 
    (pos, gen) <- get 
    let (delta, gen') = random gen 
    let pos' = pos + delta 
    put (pos', gen') 
    return pos' 

takeSteps n = evalState (replicateM n takeStep) (0, mkStdGen 0) 

यह आपके उदाहरण के रूप में ही उत्पादन का उत्पादन State B) मुझे सबसे सुंदर समाधान की तरह लगता है।


अद्यतन: इकाई ट्रांसफार्मर का उपयोग कर State monads ढेर के बारे में आपके प्रश्न का उत्तर करने के लिए: यह निश्चित रूप से संभव है। हम निम्नलिखित लिख सकते हैं, उदाहरण के लिए:

update' :: (Monad m) => Double -> StateT Double m Double 
update' x = StateT $ \y -> let z = x + y in return (z, z) 

generate' :: (Monad m) => StateT StdGen m Double 
generate' = StateT $ return . random 

takeStep' :: StateT Double (State StdGen) Double 
takeStep' = update' =<< lift generate' 

takeSteps' n = evalState (evalStateT (replicateM n takeStep') 0) $ mkStdGen 0 

हम भी विपरीत क्रम में स्टैकिंग कर सकता है।

यह संस्करण फिर से एक ही आउटपुट उत्पन्न करता है, लेकिन मेरी राय में गैर-StateT संस्करण थोड़ा स्पष्ट है।

1

2 मोनैड (और अधिकांश मोनैड के लिए एकमात्र तरीका) लिखने का सामान्य तरीका मोनैड ट्रांसफार्मर के साथ है, लेकिन विभिन्न State मोनैड के साथ आपके पास अधिक विकल्प हैं। उदाहरण के लिए: आप इन कार्यों इस्तेमाल कर सकते हैं:

leftState :: State a r -> State (a,b) r 
leftState act = state $ \ ~(a,b) -> let 
    (r,a') = runState act a 
    in (r,(a',b)) 

rightState :: State b r -> State (a,b) r 
rightState act = state $ \ ~(a,b) -> let 
    (r,b') = runState act b 
    in (r,(a,b')) 
संबंधित मुद्दे