2010-01-19 12 views
47

में स्ट्रीम के लिए उपयोग के मामले स्कैला में एक स्ट्रीम क्लास है जो एक इटेटरेटर की तरह है। विषय Difference between Iterator and Stream in Scala? दोनों के बीच समानताएं और मतभेदों में कुछ अंतर्दृष्टि प्रदान करता है।स्कैला

कैसे एक धारा का उपयोग करने देखकर बहुत सरल है, लेकिन मैं बहुत सारे आम उपयोग-मामलों में जहां मैं अन्य कलाकृतियों के बजाय एक धारा का प्रयोग करेंगे नहीं है।

विचारों मैं अभी है:

  • आप एक अनंत श्रृंखला का उपयोग करने की जरूरत है। लेकिन यह मेरे लिए एक सामान्य उपयोग-मामले की तरह प्रतीत नहीं होता है, इसलिए यह मेरे मानदंडों के अनुरूप नहीं है। (अगर यह आम है तो मुझे सही करें और मेरे पास सिर्फ एक अंधेरा स्थान है)
  • यदि आपके पास डेटा की एक श्रृंखला है जहां प्रत्येक तत्व की गणना की आवश्यकता है लेकिन आप कई बार पुन: उपयोग करना चाहेंगे। यह कमजोर है क्योंकि मैं इसे केवल एक सूची में लोड कर सकता हूं जो डेवलपर आबादी के बड़े उप-समूह के लिए अवधारणात्मक रूप से आसान है।
  • शायद वहाँ डेटा के एक बड़े सेट या एक computationally महंगा श्रृंखला है और कि इसकी प्रबल संभावना है कि आइटम आप की जरूरत के सभी तत्वों पर जाकर की आवश्यकता नहीं होगी। लेकिन इस मामले में एक इटरेटर एक अच्छा मैच होगा जब तक आपको कई खोज करने की आवश्यकता न हो, उस स्थिति में आप एक सूची का उपयोग कर सकते हैं भले ही यह थोड़ा कम कुशल हो।
  • डेटा की एक जटिल श्रृंखला है जिसे पुन: उपयोग करने की आवश्यकता है। फिर एक सूची का इस्तेमाल यहां किया जा सकता है। हालांकि इस मामले में दोनों मामलों का उपयोग करना उतना ही मुश्किल होगा और स्ट्रीम बेहतर होगा क्योंकि सभी तत्वों को लोड करने की आवश्यकता नहीं है। लेकिन फिर यह आम नहीं है ... या है ना?

तो क्या मुझे कोई बड़ा उपयोग याद आया है? या यह सबसे अधिक भाग के लिए एक डेवलपर वरीयता है?

धन्यवाद

उत्तर

40

एक Stream और एक Iterator के बीच मुख्य अंतर यह है कि बाद, परिवर्तनशील और "एक शॉट", तो बात करने के लिए है, जबकि पूर्व नहीं है। IteratorStream तुलना में एक बेहतर स्मृति पदचिह्न, लेकिन तथ्य यह है कि यह परिवर्तनशील असुविधाजनक हो सकता है है।

उदाहरण के लिए, इस क्लासिक अभाज्य संख्या जनरेटर लें:

def primeStream(s: Stream[Int]): Stream[Int] = 
    Stream.cons(s.head, primeStream(s.tail filter { _ % s.head != 0 })) 
val primes = primeStream(Stream.from(2)) 

यह आसानी से और साथ ही एक Iterator साथ लिखा जा सकता है, लेकिन एक Iterator अभाज्य संख्या अब तक की गणना की नहीं रखते होगा।

तो, एक Stream में से एक महत्वपूर्ण पहलू यह है कि आप, यह पहली दोहराया होने या यह बार-बार उत्पन्न करने के लिए बिना अन्य कार्यों के लिए इसे पारित कर सकते हैं।

महंगा संगणना/अनंत सूचियों के लिए के रूप में, इन बातों को Iterator के साथ भी किया जा सकता है। अनंत सूचियों वास्तव में काफी उपयोगी होते हैं - तुम सिर्फ यह पता नहीं है, क्योंकि आप इसे नहीं था, तो आप को देखा है एल्गोरिदम कि सख्ती से आवश्यकता से अधिक जटिल हैं बस से लागू परिमित आकार से निपटने के लिए।

+2

एक और अंतर जो मैं जोड़ना चाहता हूं वह यह है कि 'स्ट्रीम' अपने मुख्य तत्व में कभी आलसी नहीं है। 'स्ट्रीम' का प्रमुख मूल्यांकन फॉर्म में संग्रहीत किया जाता है। अगर किसी को अनुक्रम की आवश्यकता होती है जहां अनुरोध किए जाने तक कोई तत्व (सिर सहित) गणना नहीं की जाती है तो 'इटरेटर' ही एकमात्र विकल्प है। – Lii

+0

इसके अलावा यह मुख्य तत्व में आलस्य नहीं है, यह हर तत्व का भी मूल्यांकन करता है जिसे आप छोड़ना चाहते हैं। उदाहरण: '" ए "# ::" बी "# ::" सी "# ::" डी "# :: Stream.empy [स्ट्रिंग] .ड्रॉप (3) 'मूल्यांकन करेगा" ए "," बी "," सी और डी" । "डी" क्योंकि यह सिर बन जाता है। – r90t

+0

प्राइम नंबर जेनरेटर के लिए दिलचस्प संक्षिप्त उदाहरण।दिलचस्प बात यह है कि, यदि मैं इसे एक साधारण स्कैला कंसोल में बना देता हूं और फिर 4000 प्राइम के लिए पूछता हूं (अभ्यास में इतना अधिक नहीं, मेरे पास एक वैकल्पिक परिभाषा है जो 2 सेकंड से कम समय में 100K बनाता है) यह "जीसी ओवरहेड सीमा पार हो गई" त्रुटि के साथ स्कैला को दुर्घटनाग्रस्त करता है । –

17

डैनियल जवाब के अलावा, यह ध्यान रखें कि Stream कम सर्किटिंग मूल्यांकन के लिए उपयोगी है।उदाहरण के लिए, मैं कार्यों कि String लेने के लिए और Option[String] वापसी का एक विशाल सेट है लगता है, और मैं उन्हें क्रियान्वित रखना चाहते हैं जब तक उनमें से एक काम करता है:

val stringOps = List(
    (s:String) => if (s.length>10) Some(s.length.toString) else None , 
    (s:String) => if (s.length==0) Some("empty") else None , 
    (s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None 
); 

ठीक है, मैं निश्चित रूप से नहीं पर अमल करना चाहते हैं संपूर्ण सूची, और List पर कोई आसान तरीका नहीं है जो कहता है, "इन्हें फ़ंक्शंस के रूप में देखें और उनमें से एक निष्पादित करें जब तक कि उनमें से कोई None से कुछ नहीं लौटाता"। क्या करें? शायद यह:

def transform(input: String, ops: List[String=>Option[String]]) = { 
    ops.toStream.map(_(input)).find(_ isDefined).getOrElse(None) 
} 

यह एक सूची लेता है और एक Stream मानकर व्यवहार करता है (जो वास्तव में कुछ भी मूल्यांकन नहीं करता है), तो परिभाषित करता है एक नया Stream कार्यों को लागू करने का एक परिणाम है कि (लेकिन उस का मूल्यांकन नहीं करता कुछ भी अभी तक), फिर परिभाषित किया गया पहला व्यक्ति खोजता है - और यहां, जादुई रूप से, यह वापस देखता है और महसूस करता है कि इसे मानचित्र लागू करना है, और मूल सूची से सही डेटा प्राप्त करना है - और फिर इसे Option[Option[String]] से अनचाहे करना है getOrElse का उपयोग कर पर।

यहाँ एक उदाहरण है:

scala> transform("This is a really long string",stringOps) 
res0: Option[String] = Some(28) 

scala> transform("",stringOps) 
res1: Option[String] = Some(empty) 

scala> transform(" hi ",stringOps) 
res2: Option[String] = Some(hi) 

scala> transform("no-match",stringOps) 
res3: Option[String] = None 

लेकिन यह काम करता है? यदि हम अपने कार्यों में एक println डाल तो हम बता सकते हैं कि वे कहा जाता है, हम

val stringOps = List(
    (s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None }, 
    (s:String) => {println("2"); if (s.length==0) Some("empty") else None }, 
    (s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None } 
); 
// (transform is the same) 

scala> transform("This is a really long string",stringOps) 
1 
res0: Option[String] = Some(28) 

scala> transform("no-match",stringOps)      
1 
2 
3 
res1: Option[String] = None 

मिल (यह स्काला 2.8 के साथ है, 2.7 का कार्यान्वयन कभी कभी एक के बाद लक्ष्य के पार चले होगा, दुर्भाग्य और ध्यान दें कि आप। None की एक लंबी सूची जमा के रूप में अपने विफलताओं अर्जित करते हैं, लेकिन शायद यह अपने सच्चे गणना यहाँ की तुलना में सस्ता है।)

+0

मैं वास्तव में ऐसा एक उदाहरण है, लेकिन यह 'इटरेटर' के साथ आसानी से किया जा सकता है, इसलिए मैंने तय किया कि यह बिंदु के बगल में था। –

+2

अनुमोदित। मुझे यह स्पष्ट करना चाहिए था कि यह स्ट्रीम-विशिष्ट नहीं है, या एक उदाहरण चुना है जो एक ही स्ट्रीम में एकाधिक कॉल का उपयोग करता है। –

2

StreamIterator के रूप में immutable.Listmutable.List करना है। अपरिवर्तनीयता का समर्थन कभी-कभी प्रदर्शन की लागत पर, बग की एक कक्षा को रोकता है।

scalac ही इन समस्याओं के लिए प्रतिरक्षा नहीं है: http://article.gmane.org/gmane.comp.lang.scala.internals/2831

डैनियल के रूप में बताते हैं, कठोरता से अधिक आलस्य पक्ष एल्गोरिदम को आसान बनाने और यह आसान उन्हें रचना के लिए कर सकते हैं।

+1

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

7

मैं कल्पना कर सकता था कि यदि आप वास्तविक समय में कुछ डिवाइस को मतदान करते हैं, तो स्ट्रीम अधिक सुविधाजनक होता है।

एक जीपीएस ट्रैकर के बारे में सोचें, जो वास्तविक स्थिति देता है यदि आप इसे पूछते हैं। आप उस स्थान को प्रीकंप्यूट नहीं कर सकते जहां आप 5 मिनट में होंगे। आप इसे केवल कुछ मिनटों के लिए उपयोग कर सकते हैं केवल OpenStreetMap में पथ को वास्तविक बनाने के लिए या आप इसे रेगिस्तान या वर्षा वन में छह महीने से अधिक के अभियान के लिए उपयोग कर सकते हैं।

या डिजिटल थर्मामीटर या अन्य प्रकार के सेंसर जो बार-बार नए डेटा लौटाते हैं, जब तक हार्डवेयर जीवित और चालू हो जाता है - एक लॉग फ़ाइल फ़िल्टर एक और उदाहरण हो सकता है।

+1

स्ट्रीम के अच्छे उपयोग के मामले के लिए उपरोक्त। – nilskp