2011-05-31 10 views
6

स्काला में, आप क्या कर सकते हैंस्कैला संग्रह के प्रकार को क्यों बनाए रखता है Iterable (.Net में) के रूप में वापस नहीं?

val l = List(1, 2, 3) 
l.filter(_ > 2)    // returns a List[Int] 
val s = Set("hello", "world") 
s.map(_.length)    // returns a Set[Int] 

सवाल यह है: क्यों यह उपयोगी है?

स्कैला संग्रह संभवतः एकमात्र मौजूदा संग्रह ढांचा है जो ऐसा करता है। स्कैला समुदाय इस बात से सहमत है कि इस कार्यक्षमता की आवश्यकता है। फिर भी, कोई भी अन्य भाषाओं में इस कार्यक्षमता को याद नहीं करता है। उदाहरण सी # (संशोधित नामकरण स्काला के मैच के लिए):

var l = new List<int> { 1, 2, 3 } 
l.filter(i => i > 2)   // always returns Iterable[Int] 
l.filter(i => i > 2).toList // if I want a List, no problem 
l.filter(i => i > 2).toSet // or I want a Set 

नेट में, मैं हमेशा वापस एक Iterable हो और यह मेरे लिए क्या मैं इसके साथ क्या करना चाहते है। (यह .NET संग्रह को बहुत सरल बनाता है)।

सेट के साथ स्कैला उदाहरण मुझे स्ट्रिंग के सेट से लंबाई का सेट बनाने के लिए मजबूर करता है। लेकिन क्या होगा यदि मैं सिर्फ लंबाई में पुनरावृत्ति करना चाहता हूं, या लम्बाई की सूची बनाना चाहता हूं, या बाद में फ़िल्टर करने के लिए इटरबल को रखना चाहता हूं। एक सेट का निर्माण तुरंत व्यर्थ लगता है। (संपादित: collection.view सरल नेट कार्यक्षमता, अच्छा प्रदान करता है)

मुझे यकीन है कि तुम मुझे जहां नेट दृष्टिकोण बिल्कुल गलत है या प्रदर्शन को मारता उदाहरण दिखा देंगे हूँ, लेकिन मैं तो बस किसी भी नहीं देख सकते हैं (वर्षों के लिए .NET का उपयोग कर)।

+6

"यह .NET संग्रह को बहुत सरल बनाता है" मैं इसे टूटा दूंगा। जब आप 'सूची' पर कुछ संचालन करते हैं तो उसे 'सूची' क्यों नहीं लौटाया जाना चाहिए? क्या आप अनुशंसा करेंगे कि 'list.remove (1)' भी 'Iterable' देता है? => अलग राय। शायद आप सवाल थोड़ा कम तर्कवादी बना सकते हैं? – soc

+3

पहले उदाहरण में नोटिस करने के लिए एक इंटरस्टेस्टिंग चीज यह है कि 's.map (_। Length)' 'सेट' देता है जिसमें केवल एक तत्व होता है (पूर्णांक 5), क्योंकि "हैलो" और "दुनिया" दोनों की लंबाई होती है 5. – barjak

+0

@soc - मुझे लगता है, 1 हटाए गए सूची को एक सूची होना चाहिए, है ना? फ़िल्टर के मानचित्र का उपयोग करके, यह इतना स्पष्ट नहीं है कि कौन सा दृष्टिकोण बेहतर है। वैसे भी, जब आवश्यक हो तो मैं केवल विचारों का उपयोग कर सकता हूं, इसलिए समस्या हल हो गई। –

उत्तर

10

आपके प्रश्न का पूर्ण उत्तर नहीं है, लेकिन स्कैला कभी भी को एक संग्रह प्रकार का उपयोग करने के लिए मजबूर करता है। आप इस तरह कोड लिखने के लिए स्वतंत्र हैं:

import collection._ 
import immutable._ 

val s = Set("hello", "world") 
val l: Vector[Int] = s.map(_.length)(breakOut) 

एक और सवाल करने के लिए Daniel Sobral's detailed answer में breakOut बारे में अधिक पढ़ें।

यदि आप चाहते हैं अपने map या filter lazily मूल्यांकन किया जाना है, इस का उपयोग करें:

s.view.map(_.length) 

इस पूरे व्यवहार यह अपने नए समूह वर्गों को एकीकृत और कोई साथ मानक संग्रह के सभी शक्तिशाली क्षमताओं के वारिस के लिए आसान बनाता है कोड डुप्लिकेशन, यह सुनिश्चित करने के लिए कि YourSpecialCollection#filterYourSpecialCollection का एक उदाहरण देता है; YourSpecialCollection#mapYourSpecialCollection का एक उदाहरण देता है यदि यह मैप किए जाने वाले प्रकार का समर्थन करता है, या अंतर्निहित फ़ॉलबैक संग्रह का समर्थन करता है (जैसे map पर BitSet पर आप क्या कहते हैं)। निश्चित रूप से, एक सी # इटरेटर के पास .toMySpecialCollection विधि नहीं है।

यह भी देखें: The Architecture of Scala Collections में "नए सेट और मानचित्र एकीकृत करना"।

+1

धन्यवाद, विचार लगभग मेरे प्रश्न का पूरी तरह उत्तर देते हैं। मैं अभी भी यह देखने के लिए थोड़ा इंतजार करूँगा कि क्या कोई जवाब देता है कि हमेशा एक ही प्रकार का संग्रह क्यों लौटाता है, वह समझदार डिफ़ॉल्ट है। शायद यह सिर्फ स्वाद का सवाल है। –

+2

"एक सी # इटरेटर के पास नहीं है .toMySpecialCollection" - आप मेरी लाइब्रेरी पैटर्न (जिसे सी # में एक्सटेंशन विधियों कहा जाता है) का उपयोग कर सकते हैं। विस्तार विधियों में प्रति कॉल अतिरिक्त आवंटन शामिल नहीं है। –

+4

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

7

यदि आपके पास Set है, और उस पर एक ऑपरेशन Iterable देता है जबकि इसके रनटाइम प्रकार अभी भी Set है, तो आप इसके व्यवहार के बारे में महत्वपूर्ण जानकारी खो सकते हैं, और सेट-विशिष्ट विधियों तक पहुंच खो सकते हैं।

बीटीडब्ल्यू: अन्य भाषाएं समान हैं, जैसे हास्केल, जो स्कैला को बहुत प्रभावित करती है।map की हास्केल संस्करण (implicit जादू के बिना) इस स्काला के अनुवाद दिखाई देगा:

//the functor type class 
trait Functor[C[_]] { 
    def fmap[A,B](f: A => B, coll: C[A]) : C[B] 
} 

//an instance 
object ListFunctor extends Functor[List] { 
    def fmap[A,B](f: A => B, list: List[A]) : List[B] = list.map(f) 
} 

//usage 
val list = ListFunctor.fmap((x:Int) => x*x, List(1,2,3)) 

और मैं हास्केल समुदाय इस सुविधा को महत्व देता है और साथ ही :-)

9

स्काला "वर्दी वापसी प्रकार इस प्रकार है लगता है सिद्धांत "यह सुनिश्चित करता है कि सी # में उस जानकारी को खोने के बजाय आप हमेशा उचित रिटर्न प्रकार के साथ समाप्त हो जाएं।

कारण सी # ऐसा कारण यह है कि उनका प्रकार प्रणाली प्रत्येक आवेदक में प्रत्येक विधि के पूरे कार्यान्वयन को ओवरराइड किए बिना इन आश्वासनों को प्रदान करने के लिए पर्याप्त नहीं है। स्कैला इसे उच्च प्रकार के प्रकार के उपयोग के साथ हल करता है।

क्यों स्कैला के पास ऐसा एकमात्र संग्रह ढांचा है? क्योंकि यह मुश्किल से ज्यादातर लोगों को लगता है यह, है, खासकर जब तार और सरणी तरह बातें जो कोई 'असली' संग्रह के रूप में अच्छी तरह से एकीकृत किया जाना चाहिए रहे हैं:

// This stays a String: 
scala> "Foobar".map(identity) 
res27: String = Foobar 
// But this falls back to the "nearest" appropriate type: 
scala> "Foobar".map(_.toInt) 
res29: scala.collection.immutable.IndexedSeq[Int] = Vector(70, 111, 111, 98, 97, 114) 
4

यह स्थिरता की बात है। चीजें वे हैं जो हैं, और उनके जैसे चीजें वापस। आप इस पर निर्भर कर सकते हैं।

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

def print5(it: Iterable[Int]) = { 
    var flag = true 
    it.filter(_ => flag).foreach { i => 
     flag = i < 5 
     println(i) 
    } 
} 

टेस्ट यह: इस सरल उदाहरण लें

print5(List.range(1, 10)) 
print5(Stream.range(1, 10)) 

यहाँ, List सख्त है, इसलिए इसकी तरीकों सख्त हैं। इसके विपरीत, Stream गैर-सख्त है, इसलिए इसकी विधियां गैर-सख्त हैं।

तो यह वास्तव में Iterable से संबंधित नहीं है सब पर - सब के बाद, दोनों List और StreamIterable हैं। संग्रह रिटर्न प्रकार को बदलने से सभी प्रकार की समस्याएं हो सकती हैं - कम से कम, यह लगातार डेटा संरचना को कठिन रखने का कार्य करेगी।

दूसरी तरफ, सख्त संग्रह पर भी कुछ संचालन में देरी के फायदे हैं। इसे करने के कुछ तरीके यहां दिए गए हैं:

// Get an iterator explicitly, if it's going to be used only once 
def print5I(it: Iterable[Int]) = { 
    var flag = true 
    it.iterator.filter(_ => flag).foreach { i => 
     flag = i < 5 
     println(i) 
    } 
} 

// Get a Stream explicitly, if the result will be reused 
def print5S(it: Iterable[Int]) = { 
    var flag = true 
    it.toStream.filter(_ => flag).foreach { i => 
     flag = i < 5 
     println(i) 
    } 
} 

// Use a view, which provides non-strictness for some methods 
def print5V(it: Iterable[Int]) = { 
    var flag = true 
    it.view.filter(_ => flag).foreach { i => 
     flag = i < 5 
     println(i) 
    } 
} 

// Use withFilter, which is explicitly designed to be used as a non-strict filter 
def print5W(it: Iterable[Int]) = { 
    var flag = true 
    it.withFilter(_ => flag).foreach { i => 
     flag = i < 5 
     println(i) 
    } 
} 
संबंधित मुद्दे