16

के रूप में डेटा प्रोसेसिंग का प्रतिनिधित्व करने के लिए ढांचे, अधिकांश डेटा प्रोसेसिंग को घटकों की पाइपलाइन के रूप में देखा जा सकता है, एक दूसरे के इनपुट में भोजन करने का उत्पादन। एक ठेठ प्रसंस्करण पाइप लाइन है:पाइपलाइन

reader | handler | writer 

एक पन्नी के रूप में यह चर्चा शुरू करने के लिए, चलो इस पाइपलाइन जहां प्रत्येक खंड एक वस्तु है की एक वस्तु उन्मुख कार्यान्वयन पर विचार करें। handler वस्तु दोनों reader और writer वस्तुओं के संदर्भ हैं और एक run विधि है जो लगता है कि है:

define handler.run: 
    while (reader.has_next) { 
    data = reader.next 
    output = ...some function of data... 
    writer.put(output) 
    } 

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

reader <- handler -> writer 

अब मान लीजिए कि मैं एक नई पाइपलाइन खंड लगाना चाहता हूँ पाठक और हैंडलर के बीच:

reader | tweaker | handler | writer 

फिर से, इस ओओ में कार्यान्वयन, tweakerreader वस्तु के चारों ओर एक आवरण होगा, और tweaker तरीकों की तरह कुछ लग सकता है (कुछ छद्म जरूरी कोड में):

define tweaker.has_next: 
    return reader.has_next 

define tweaker.next: 
    value = reader.next 
    result = ...some function of value... 
    return result 

मैं खोजने कर रहा हूँ कि यह एक बहुत composable अमूर्त नहीं है। कुछ मुद्दे हैं:

  1. tweaker केवल handler के बाएं हाथ की ओर इस्तेमाल किया जा सकता है, यानी मैं इस पाइप लाइन के लिए फार्म tweaker के ऊपर कार्यान्वयन का उपयोग नहीं कर सकते हैं:

    पाठक | हैंडलर | ट्वीकर | लेखक

  2. मैं, पाइपलाइनों के साहचर्य संपत्ति का दोहन करना चाहते हैं तो यह पाइपलाइन कि:

    पाठक | हैंडलर | लेखक

व्यक्त किया जा सकता है:

reader | p 

जहां p पाइपलाइन handler | writer है। इस OO कार्यान्वयन में मैं आंशिक रूप से handler वस्तु का दृष्टांत को

  1. की (1), वस्तुओं एक पुन: कथन की कुछ हद तक पता करने के लिए होता है, तो वे "पुश" या "पुल" डेटा।

मैं डेटा प्रोसेसिंग पाइपलाइन बनाने के लिए एक ढांचा (अनिवार्य रूप से ओओ) की तलाश में हूं जो इन मुद्दों को संबोधित करता है।

मैंने इसे Haskell और functional programming के साथ टैग किया है क्योंकि मुझे लगता है कि कार्यात्मक प्रोग्रामिंग अवधारणाएं यहां उपयोगी हो सकती हैं।

एक लक्ष्य के रूप में, यह इस तरह से एक पाइप लाइन बनाने के लिए सक्षम होने के लिए अच्छा होगा:

     handler1 
       /  \ 
reader | partition    writer 
        \  /
        handler2 

कुछ परिप्रेक्ष्य के लिए, यूनिक्स शेल पाइप निम्नलिखित कार्यान्वयन निर्णय के साथ इन समस्याओं का एक बहुत हल करती है:

  1. पाइपलाइन घटक अलग प्रक्रियाओं

  2. पाइप वस्तुओं में अतुल्यकालिक रूप से चलाने के "pushers" और "pulle के बीच डेटा गुजर मध्यस्थता रु "; यानी वे लेखकों को अवरुद्ध करते हैं जो डेटा बहुत तेजी से लिखते हैं और पाठक जो बहुत तेज़ी से पढ़ने की कोशिश करते हैं।

  3. आप पाइपलाइन

मैं विशेष रूप से दृष्टिकोण जो सूत्रण या एजेंटों के बीच संदेश-गुजर उपयोग नहीं करते हैं में दिलचस्पी के लिए निष्क्रिय घटकों (अर्थात फ़ाइलें) कनेक्ट करने के लिए विशेष कनेक्टर्स < और > का उपयोग करें। शायद यह करने का सबसे अच्छा तरीका है, लेकिन यदि संभव हो तो मैं थ्रेडिंग से बचना चाहता हूं।

धन्यवाद!

+9

http://www.haskell.org/arrows –

+1

पर एक नज़र डालें, शायद आप कुछ पाठक, एक पाठक, ट्वीकर, हैंडलर और लेखक के लिए एक थ्रेड बनाना चाहते हैं, और [चान की ' ] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Concurrent-Chan.html)? मुझे 100% यकीन नहीं है कि मैं समझता हूं कि शीर्ष-स्तर का सवाल क्या है, हालांकि ... –

+0

अभी तक, अंतिम चित्र 'पाठक >>> विभाजन >>> हैंडलर 1 *** हैंडलर 2 >>> लेखक' जैसा दिखता है, लेकिन शायद कुछ जटिलताओं को और अधिक जटिल बनाने की आवश्यकता होगी। –

उत्तर

20

हाँ, arrows लगभग निश्चित रूप से आपका आदमी है।

मुझे संदेह है कि आप अपने प्रश्न में जो बातें कह रहे हैं, उसके आधार पर आप हास्केल के लिए बिल्कुल नए हैं। तीर शायद काफी अमूर्त प्रतीत होंगे, खासकर अगर आप जो खोज रहे हैं वह एक "ढांचा" है। मुझे पता है कि तीर के साथ क्या चल रहा था वास्तव में मुझे यह समझने में थोड़ी देर लग गई।

तो आप उस पृष्ठ को देख सकते हैं और कह सकते हैं "हाँ, ऐसा लगता है कि मैं क्या चाहता हूं", और फिर समस्या को हल करने के लिए तीरों का उपयोग शुरू करने के तरीके के रूप में खुद को खो दिया। तो यहां थोड़ा सा मार्गदर्शन है ताकि आप जान सकें कि आप क्या देख रहे हैं।

तीर आपकी समस्या का समाधान नहीं करेंगे। इसके बजाए, वे आपको एक ऐसी भाषा देते हैं जिसका आप उपयोग कर सकते हैं जिसमें आप अपनी समस्या का वाक्यांश देते हैं। आप पाते हैं कि कुछ पूर्वनिर्धारित तीर नौकरी करेगा - कुछ क्लेस्ली तीर शायद - लेकिन उस दिन के अंत में आप लागू करने के लिए एक तीर (पूर्वनिर्धारित वाले बस आपको उन्हें लागू करने के आसान तरीके) जो "डेटा प्रोसेसर" द्वारा आपका मतलब क्या व्यक्त करता है। लगभग मामूली उदाहरण के रूप में, मान लीजिए कि आप सरल डेटा द्वारा अपने डेटा प्रोसेसर को कार्यान्वित करना चाहते हैं। आप लिखते थे:

newtype Proc a b = Proc { unProc :: a -> b } 

-- I believe Arrow has recently become a subclass of Category, so assuming that. 

instance Category Proc where 
    id = Proc (\x -> x) 
    Proc f . Proc g = Proc (\x -> f (g x)) 

instance Arrow Proc where 
    arr f = Proc f 
    first (Proc f) = Proc (\(x,y) -> (f x, y)) 

यह आपको मशीनरी विभिन्न तीर combinators (***), (&&&), (>>>), आदि का उपयोग करने, साथ ही तीर अंकन जो बल्कि अच्छा है अगर आप जटिल चीज़ें कर रहे हैं देता है। तो, के रूप में डैनियल फिशर टिप्पणी में बताते हैं, पाइपलाइन आप अपने प्रश्न में वर्णित के रूप बना जा सकता है:

reader >>> partition >>> (handler1 *** handler2) >>> writer 

लेकिन अच्छी बात यह है कि यह आपको एक प्रोसेसर के द्वारा क्या मतलब है आप पर निर्भर है।

newtype Proc' a b = Proc (Source a -> Sink b -> IO()) 

और फिर उचित रूप से लागू करने combinators: यह लागू करने के लिए क्या आप प्रत्येक प्रोसेसर एक समान तरीके से एक धागा forking, एक अलग प्रोसेसर प्रकार का उपयोग कर के बारे में उल्लेख किया जा सकता है।

तो यही वह है जो आप देख रहे हैं: प्रक्रियाओं को लिखने के बारे में बात करने के लिए एक शब्दावली, जिसमें पुन: उपयोग करने के लिए थोड़ा सा कोड है, लेकिन मुख्य रूप से आपकी सोच को मार्गदर्शन करने में मदद करेगा क्योंकि आप इन संयोजकों को प्रोसेसर की परिभाषा के लिए लागू करते हैं आपके डोमेन में उपयोगी

मेरी पहली नॉनट्रिविअल हास्केल परियोजनाओं में से एक arrow for quantum entanglement लागू करना था; वह प्रोजेक्ट वह था जिसने मुझे अपने प्रोग्रामिंग कैरियर में एक प्रमुख मोड़ के बारे में सोचने के लिए हास्केल तरीके को समझना शुरू कर दिया। शायद तुम्हारी यह परियोजना आपके लिए भी वही करेगी? :-)

7

आलसी मूल्यांकन के लिए धन्यवाद, हम हास्केल में सामान्य कार्य रचनाओं के संदर्भ में पाइपलाइनों को व्यक्त कर सकते हैं। यहाँ एक उदाहरण है कि एक फ़ाइल में एक पंक्ति की अधिकतम लंबाई की गणना करता है:

main = interact (show . maximum . map length . lines) 

यहाँ में सब कुछ उदाहरण

lines :: String -> [String] 

लेकिन आलसी मूल्यांकन करने के लिए धन्यवाद, इन समारोह केवल प्रक्रिया के लिए की तरह, एक साधारण कार्य है इनपुट वृद्धिशील रूप से और केवल उतना ही आवश्यक है जितना कि एक यूनिक्स पाइप होगा।

+0

इस उदाहरण के लिए धन्यवाद। – ErikR

4

हास्केल के लिए enumerator package इस के लिए एक अच्छा ढांचा है। यह तीन प्रकार की वस्तुओं को परिभाषित करता है:

  1. संख्याएं जो डेटा में डेटा उत्पन्न करती हैं।
  2. Iteratees जो डेटा के हिस्सों का उपभोग करते हैं और पर्याप्त उपभोग करने के बाद एक मूल्य वापस करते हैं।
  3. अंकुश जो पाइपलाइन के बीच में बैठते हैं। वे टुकड़ों का उपभोग करते हैं और संभवतः साइड इफेक्ट्स के साथ भाग का उत्पादन करते हैं।

इन तीन प्रकार की वस्तुओं को स्ट्रीम प्रोसेसिंग पाइपलाइन में बनाया गया है, और आप एक पाइपलाइन में कई एन्यूमरेटर्स और इटेटेट्स भी कर सकते हैं (जब कोई समाप्त हो जाता है, तो अगला इसकी जगह लेता है)। स्क्रैच से इन वस्तुओं में से किसी एक को लिखना जटिल हो सकता है, लेकिन बहुत से संयोजक हैं जिनका उपयोग नियमित रूप से डेटा स्ट्रीम प्रोसेसर में बदलने के लिए किया जा सकता है। उदाहरण के लिए, इस पाइप लाइन, stdin से सभी पात्रों पढ़ता उन्हें समारोह toUpper साथ अपर केस में बदलता है, तो उन्हें stdout में लिखते हैं:

ET.enumHandle stdin $$ ET.map toUpper =$ ET.iterHandle stdout 

जहां मॉड्यूल Data.Enumerator.TextET के रूप में आयात किया गया है।

+2

हैकेज पर कई गणराज्य-शैली पैकेज हैं; ओपी को iter-io (http://hackage.haskell.org/package/iterIO) में रुचि हो सकती है जो स्पष्ट रूप से यूनिक्स शैल पाइपलाइनों पर आधारित है। –

2

Yesod ढांचा conduit पैकेज के रूप में एक हास्केल पाइप लाइब्रेरी का उपयोग करता है।

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