2015-11-26 9 views
14

मेरी प्रोग्राम की स्थिति, तीन मानों, a, b, और c के होते प्रकार A, B, और C के भीतर स्टेटफुल संगणना से मेल खाते हैं। विभिन्न कार्यों को विभिन्न मूल्यों तक पहुंच की आवश्यकता होती है। मैं State मोनैड का उपयोग करके फ़ंक्शन लिखना चाहता हूं ताकि प्रत्येक कार्य केवल उस राज्य के टुकड़ों तक पहुंच सके जिसे इसे एक्सेस करने की आवश्यकता हो।मिक्स और राज्य इकाई

मैं निम्नलिखित प्रकार के चार कार्य किया है:

f :: State (A, B, C) x 
g :: y -> State (A, B) x 
h :: y -> State (B, C) x 
i :: y -> State (A, C) x 

यहाँ है कि कैसे मैं gf भीतर फोन: g' समारोह बॉयलरप्लेट का एक बदसूरत टुकड़ा है कि लेकिन पुल कुछ नहीं करता है

f = do 
    -- some stuff 
    -- y is bound to an expression somewhere in here 
    -- more stuff 
    x <- g' y 
    -- even more stuff 

    where g' y = do 
       (a, b, c) <- get 
       let (x, (a', b')) = runState (g y) (a, b) 
       put (a', b', c) 
       return x 

कि (A, B, C) और (A, B) के बीच का अंतर। यह मूल रूप से g का एक संस्करण है जो 3-ट्यूपल राज्य पर चलता है, लेकिन तीसरे ट्यूपल आइटम को अपरिवर्तित छोड़ देता है। मैं उस बॉयलरप्लेट के बिना f लिखने का एक तरीका ढूंढ रहा हूं। इस तरह हो सकता है कि कुछ:

f = do 
    -- stuff 
    x <- convert (0,1,2) (g y) 
    -- more stuff 

कहाँ convert (0,1,2) प्रकार State (a, b) x की गणना धर्मान्तरित टाइप करने के लिए State (a, b, c) x। इसी तरह, सभी प्रकार a, b, c, d के लिए:

  • convert (2,0,1)State (c,a) xState (a,b,c) x करने के लिए
  • convert (0,1) धर्मान्तरित State b xState (a,b) x-
  • convert (0,2,1,0) धर्मान्तरित State (c,b) xState (a,b,c,d) x करने के लिए

धर्मान्तरित मेरे प्रश्न:

  1. क्या राज्य मूल्यों को टुपल्स में डालने से बेहतर समाधान है? मैंने एक मोनड ट्रांसफॉर्मर स्टैक का उपयोग करने के बारे में सोचा। हालांकि, मुझे लगता है कि केवल तभी काम करता है, किसी भी दो कार्य f और g के लिए, या तो FG या GF, जहां F राज्य f और G द्वारा आवश्यक मूल्यों का वह समूह है g द्वारा आवश्यक राज्य मूल्यों का वह समूह है। क्या मैं उस बारे में गलत हूं? (ध्यान दें कि मेरे उदाहरण इस संपत्ति को पूरा नहीं करता है। उदाहरण के लिए, G = {a, b} और H = {b, c}। न तो अन्य के एक सबसेट है।)
  2. अगर वहाँ tuples के अलावा कोई बेहतर तरीका है, तो करने के लिए एक अच्छा तरीका है मैंने उल्लेख किया बॉयलरप्लेट से बचें? मैं बॉयलरप्लेट फ़ंक्शंस (नीचे देखें) के एक समूह के साथ एक फ़ाइल लिखने के लिए भी तैयार हूं, जब तक कि फ़ाइल स्वचालित रूप से एक बार उत्पन्न हो और फिर भूल जाए। क्या कोई बेहतर तरीका है? (मैंने लेंस के बारे में पढ़ा है, लेकिन उनकी जटिलता, बदसूरत वाक्यविन्यास, अनावश्यक सुविधाओं का विशाल सेट, और टेम्पलेट हास्केल पर निर्भर निर्भरता बंद है। क्या यह मेरी गलत धारणा है? क्या लेंस मेरी समस्या को इस तरह से हल कर सकते हैं जो उन समस्याओं से बचाता है ?)

(मेरे द्वारा वर्णित कार्यों को इस तरह कुछ दिखाई देगा।)

convert_0_1_2 :: State (a, b) x -> State (a, b, c) x 
convert_0_1_2 f = do 
    (a, b, c) <- get 
    let (x, (a', b')) = runState f (a, b) 
    put (a', b', c) 
    return x 

convert_0_2_1_0 :: State (c, b) x -> State (a, b, c, d) x 
convert_0_2_1_0 f = do 
    (a, b, c, d) <- get 
    let (x, (b', c')) = runState f (b, c) 
    put (a, b', c', d) 
    return x 
+5

मैं इसे अपने आप उपयोग नहीं किया है, लेकिन यह 'तरह lens' के' zoom' बहुत ज्यादा लगता है: http://hackage.haskell.org/package/lens-4.13/docs/Control-Lens-Zoom.html #v: ज़ूम –

+1

यह निर्भर प्रकारों के साथ आसान होना चाहिए, लेकिन हैशोकिस्ट नहीं है। – user3237465

+0

'lens' + [' vinyl'] (https://hackage.haskell.org/package/vinyl) एक आधे रास्ते सभ्य hasochistic अनुभव के लिए बनाता है। 'च :: राज्य (ए, ए)()' और 'जी :: राज्य ए()': –

उत्तर

9

आप lens-family से ज़ूम या tuple-lenses पैकेज के साथ lens पैकेज का उपयोग करके यह कर सकते हैं: zoom के सरलीकृत प्रकार है:

zoom :: Lens' s a -> State a x -> State s x 

तो zoom एक गणना एक छोटे राज्य का उपयोग चलाता है। Lens बड़ा राज्य s अंदर छोटे राज्य a का स्थान निर्दिष्ट करने के लिए प्रयोग किया जाता है।

इन दो संकुल के साथ

, आप g, h और i इस प्रकार चला सकते हैं:

f :: State (A,B,C) x 
f = do 
    zoom _12 g -- _12 :: Lens' (A,B,C) (A,B) 
    zoom _23 h -- _23 :: Lens' (A,B,C) (B,C) 
    zoom _13 i -- _13 :: Lens' (A,B,C) (A,C) 
+0

धन्यवाद, कि क्या मैं :-) –

4

आप tuples के साथ आसपास उपद्रव नहीं करना चाहते हैं, तो आप एक रिकार्ड के साथ एक "उत्तम दर्जे का" दृष्टिकोण का उपयोग कर सकते । वहाँ कुछ फैंसी खाका हास्केल lens पैकेज में इस समर्थन करने के लिए है, लेकिन आप यह हाथ से भी कर सकते हैं। विचार राज्य के प्रत्येक टुकड़े के लिए कम से कम एक वर्ग बनाने के लिए है:

class HasPoints s where 
    points :: Lens' s Int 

class ReadsPoints s where 
    getPoints :: Getter s Int 
    default getPoints :: HasPoints s => Getter s Int 
    getPoints = points 

class SetsPoints s where 
    setPoints :: Setter' s Int 
    ... 

फिर प्रत्येक समारोह manipulates कि राज्य की तरह

fight :: (HasPoints s, ReadsHealth s) => StateT s Game Player 

इस विशेष हस्ताक्षर के साथ एक कार्रवाई एक प्रकार हस्ताक्षर करना होगा है अंक तक पूर्ण पहुंच, और स्वास्थ्य के लिए केवल पढ़ने के लिए उपयोग।

+0

मेरा आम उपयोग के मामले के लिए लक्ष्य था। 'एफ' के भीतर, मैं' g' को 'f' के राज्य के पहले या दूसरे भाग पर' g' कॉल करने में सक्षम होना चाहता हूं। 'जी 'को नहीं पता होना चाहिए कि इनमें से कौन से टुकड़े इसमें से गुजर रहे हैं। 'जी 'को यह भी नहीं पता होना चाहिए कि यह एक बड़े आवेदन का हिस्सा है। लेकिन उत्तम दर्जे का समाधान है, साथ 'f' और' g' ही राज्य का प्रकार (एक 'Game' मूल्य) है और सही है कि राज्य के किसी भी टुकड़ा तक पहुँचने के लिए एक ही getters/setters का प्रयोग करेंगे? हालांकि इससे मुझे कुछ जिम्मेदारियों के साथ एक समारोह से जानकारी छिपाने की सुविधा मिलती है, ऐसा लगता है कि मेरे कार्यों को अप्राप्य बना दिया जाता है। – Jordan

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