2012-11-04 9 views
10

एक StackOverflow प्रश्न का उत्तर में मैं एक वैल के रूप में एक धारा का निर्माण किया है, तो जैसे:क्या मुझे स्ट्रीम को परिभाषित करते समय वैल या डीफ़ का उपयोग करना चाहिए?

val s:Stream[Int] = 1 #:: s.map(_*2) 

और किसी ने मुझे बताया कि डीईएफ़के बजाय इस्तेमाल किया जाना चाहिए वैल क्योंकि स्काला काटा शिकायत (करता है ग्रहण में स्कैला वर्कशीट) कि "आगे का संदर्भ मानों की परिभाषा पर विस्तारित करता है।"

लेकिन स्ट्रीम डॉक्स में उदाहरण वैल का उपयोग करें। कौनसा सही हैं?

उत्तर

21

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

इस कार्यक्रम के इस तरह से डीईएफ़ का उपयोग कर के साथ समस्या यह दर्शाता है:

// Show the difference between the use of val and def with Streams. 

object StreamTest extends App { 

    def sum(p:(Int,Int)) = { println("sum " + p); p._1 + p._2 } 

    val fibs1: Stream[Int] = 0 #:: 1 #:: (fibs1 zip fibs1.tail map sum) 
    def fibs2: Stream[Int] = 0 #:: 1 #:: (fibs2 zip fibs2.tail map sum) 

    println("========== VAL ============") 
    println("----- Take 4:"); fibs1 take 4 foreach println 
    println("----- Take 5:"); fibs1 take 5 foreach println 

    println("========== DEF ============") 
    println("----- Take 4:"); fibs2 take 4 foreach println 
    println("----- Take 5:"); fibs2 take 5 foreach println 
} 

यहाँ आउटपुट है:

========== VAL ============ 
----- Take 4: 
0 
1 
sum (0,1) 
1 
sum (1,1) 
2 
----- Take 5: 
0 
1 
1 
2 
sum (1,2) 
3 
========== DEF ============ 
----- Take 4: 
0 
1 
sum (0,1) 
1 
sum (0,1) 
sum (1,1) 
2 
----- Take 5: 
0 
1 
sum (0,1) 
1 
sum (0,1) 
sum (1,1) 
2 
sum (0,1) 
sum (0,1) 
sum (1,1) 
sum (1,2) 
3 

सूचना है कि जब हम वैल प्रयोग किया है:

  • " 5 ले लो "" ले 4 "द्वारा गणना मूल्यों को दोबारा नहीं बदला।
  • "ले 4" में 4 मूल्य कम्प्यूटिंग 3 मूल्य का कारण नहीं की पुनर्गणना किया जाना है।

लेकिन उन में से कोई भी सच है जब हम डीईएफ़ का उपयोग करें। अपने स्वयं के रिकर्सन सहित स्ट्रीम का हर उपयोग, एक नए स्ट्रीम के साथ खरोंच से शुरू होता है। चूंकि एनएच मूल्य के उत्पादन के बाद से पहले हम एन -1 और एन -2 के मूल्यों का उत्पादन करते हैं, जिनमें से प्रत्येक को अपने दो पूर्ववर्तियों का उत्पादन करना चाहिए और इसी तरह, मूल्य का उत्पादन करने के लिए आवश्यक राशि() की संख्या बहुत बढ़ जाती है फिबोनाची अनुक्रम स्वयं: 0, 0, 1, 2, 4, 7, 12, 20, 33, .... और चूंकि उन सभी स्ट्रीम एक ही समय में ढेर पर हैं, हम जल्दी से स्मृति से बाहर निकलते हैं।

तो खराब प्रदर्शन और स्मृति मुद्दों को देखते हुए, आप आम तौर पर नहीं एक स्ट्रीम बनाने में डीईएफ़ उपयोग करना चाहते हैं।

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

scala> val randInts = Stream.continually(util.Random.nextInt(100)) 
randInts: scala.collection.immutable.Stream[Int] = Stream(1, ?) 

scala> (randInts take 1000).sum 
res92: Int = 51535 

scala> (randInts take 1000).sum 
res93: Int = 51535     <== same answer as before, from saved values 

scala> def randInts = Stream.continually(util.Random.nextInt(100)) 
randInts: scala.collection.immutable.Stream[Int] 

scala> (randInts take 1000).sum 
res94: Int = 49714 

scala> (randInts take 1000).sum 
res95: Int = 48442     <== different Stream, so new answer 

randInts एक विधि करने के लिए हमें का कारण बनता है बनाना: उस मामले में यह डीईएफ़ उपयोग करने के लिए इतना है कि आप एक नया स्ट्रीम हर बार मिलता है और इसे करने के लिए पर पकड़ नहीं है, इतना है कि यह कचरा-एकत्र किया जा सकता है समझ में आता है प्रत्येक बार एक नया स्ट्रीम प्राप्त करें, इसलिए हमें नए मान मिलते हैं, और स्ट्रीम एकत्र किया जा सकता है।

सूचना है कि यह केवल भावना डीईएफ़ यहाँ उपयोग करने के लिए है, क्योंकि नए मूल्यों पुराने मूल्यों पर निर्भर नहीं है बनाता है, इसलिए randInts खुद के रूप में परिभाषित नहीं है।Stream.continually ऐसे स्ट्रीम बनाने का एक आसान तरीका है: आप बस इसे बताएं कि मूल्य कैसे बनाएं और यह आपके लिए स्ट्रीम बनाता है।

+0

क्या आप वाकई एक प्रेजेंटेशन कंपाइलर सीमा है और न केवल एक क्षेत्र बनाम स्थानीय परिवर्तनीय चीज़ है? –

+1

अच्छा बिंदु, लुइगी। मैंने आपकी टिप्पणी पढ़ने के बाद कुछ और प्रयोग किया और अब मुझे नहीं लगता कि मैं पूरी तरह से समझता हूं कि समस्या क्या है, लेकिन मुझे लगता है कि यह उन उपकरणों से संबंधित है जो कोड को लपेटते हैं। मुझे स्केल वर्कशीट में ऑब्जेक्ट में त्रुटि मिलती है, लेकिन कक्षा में नहीं, और दोनों स्केलेक के साथ काम करते हैं। मैं पीसी को दोष न देने के उत्तर को संशोधित करूंगा। – AmigoNico

+0

यदि आप इसे किसी ऑब्जेक्ट से लपेटते हैं तो यह ठीक काम करता है: http://www.scalakata.com/50975187e4b093f3524f3685 –

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

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