स्कैला सूची संघन, ::: बनाम ++


362

क्या स्काला में सूची को बदलने के लिए :::और इसके बीच कोई अंतर है ++?

scala> List(1,2,3) ++ List(4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)

scala> List(1,2,3) ::: List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)

scala> res0 == res1
res2: Boolean = true

से प्रलेखन ऐसा लगता है कि ++अधिक सामान्य है, जबकि है :::है Listविशिष्ट। क्या उत्तरार्द्ध प्रदान किया गया है क्योंकि इसका उपयोग अन्य कार्यात्मक भाषाओं में किया जाता है?


4
इसके अलावा :::एक उपसर्ग ऑपरेटर है जो सभी तरीकों से शुरू होता है:
बेन जैक्सन

3
जवाब बहुत ज्यादा है जिस तरह से स्काला सूचियों और ऑपरेटर की एकरूपता (या बाद की कमी) के आसपास विकसित हुआ था। यह थोड़ा दुर्भाग्यपूर्ण है कि किसी भी स्काला शिक्षार्थी के समय को भ्रमित करने और बर्बाद करने के लिए कुछ इतना सरल है कि मीनुटिया की इतनी लंबी पूंछ है। मेरी इच्छा है कि इसे 2.12 में समतल कर दिया जाए।
1

जवाबों:


321

विरासत। सूची को मूल रूप से कार्यात्मक भाषाओं के रूप में परिभाषित किया गया था:

1 :: 2 :: Nil // a list
list1 ::: list2  // concatenation of two lists

list match {
  case head :: tail => "non-empty"
  case Nil          => "empty"
}

बेशक, स्काला ने एक तदर्थ तरीके से, अन्य संग्रह विकसित किए। जब 2.8 बाहर आया था, संग्रह अधिकतम कोड पुन: उपयोग और लगातार एपीआई के लिए बदल दिया गया है, ताकि आप उपयोग कर सकते हैं ++श्रेणीबद्ध करने के लिए किसी भी दो संग्रह - और यहां तक कि iterators। हालाँकि, सूची को अपने मूल संचालकों को एक या दो से अलग रखने के लिए मिला, जो पदावनत हो गए।


19
तो क्या अब के :::पक्ष में बचने के लिए यह सबसे अच्छा अभ्यास है ++? इसके +:बजाय का उपयोग करें ::?
लुइगी प्लिंज

37
::पैटर्न मिलान के कारण उपयोगी है (डैनियल दूसरा उदाहरण देखें)। आप ऐसा नहीं कर सकते हैं+:
प्रतिमान

1
@ लुइगी यदि आप Listइसके बजाय उपयोग कर रहे हैं Seq, तो आप मुहावरेदार Listतरीकों का भी उपयोग कर सकते हैं । दूसरी ओर, यदि आप कभी ऐसा करने की इच्छा रखते हैं, तो इसे दूसरे प्रकार में बदलना कठिन हो जाएगा ।
डैनियल सी। सोबरल

2
मैं इसे अच्छा लगता है एक दोनों सूची मुहावरेदार कार्य किया है (जैसे कि ::और :::) और अधिक सामान्य ऑपरेशन है, जिसमें अन्य संग्रह करने के लिए आम बात है। मैं भाषा से या तो ऑपरेशन नहीं छोड़ूंगा।
जियोर्जियो

21
@paradigmatic Scala 2.10 में :++:ऑब्जेक्ट एक्सट्रैक्टर्स हैं।
__ १

97

हमेशा उपयोग करें :::। दो कारण हैं: दक्षता और प्रकार की सुरक्षा।

दक्षता

x ::: y ::: zसे बेहतर है x ++ y ++ z, क्योंकि :::सही सहयोगी है। x ::: y ::: zके रूप में पार्स किया जाता है x ::: (y ::: z), जो एल्गोरिदमिक रूप से तेज़ है (x ::: y) ::: z(बाद वाले को ओ (| x |) अधिक चरणों की आवश्यकता है)।

प्रकार की सुरक्षा

:::आप के साथ केवल दो Listएस समेट सकते हैं । साथ ++आप किसी भी संग्रह करने के लिए जोड़ सकते हैं List, जो भयानक है:

scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)

++के साथ मिश्रण करना भी आसान है +:

scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab

9
जब सिर्फ 2 सूचियों को समेटना, कोई अंतर नहीं है, लेकिन 3 या अधिक के मामले में, आपके पास एक अच्छा बिंदु है, और मैंने एक त्वरित बेंचमार्क के साथ इसकी पुष्टि की है। हालांकि, यदि आप दक्षता के बारे में चिंतित हैं, तो x ::: y ::: zइसे बदल दिया जाना चाहिए List(x, y, z).flattenpastebin.com/gkx7Hpad
लुइगी प्लिंज

3
कृपया बताएं, क्यों बाएं साहचर्य संघटन के लिए अधिक ओ (x) चरणों की आवश्यकता होती है। मुझे लगा कि दोनों ओ (1) के लिए काम करते हैं।
pacman

6
@pacman सूचियां अकेले जुड़ी हुई हैं, एक सूची को दूसरे में जोड़ने के लिए आपको पहली सूची की एक प्रति बनाने की आवश्यकता है जिसमें अंत में दूसरा एक संलग्न है। पहली सूची में तत्वों की संख्या के संबंध में इसलिए ओ (एन) है। दूसरी सूची की लंबाई रनटाइम को प्रभावित नहीं करती है, इसलिए एक लंबी सूची को एक छोटी सूची में जोड़ने के बजाय एक छोटी सूची को एक लंबी सूची में जोड़ना बेहतर है।
पुहलेन

1
@pacman Scala की सूचियाँ अपरिवर्तनीय हैं । इसलिए हम केवल अंतिम लिंक को बदलने के लिए नहीं कर सकते हैं। हमें स्क्रैच से एक नई सूची बनानी चाहिए।
ZhekaKozlov

4
@pacman जटिलता हमेशा रेखीय wrt की लंबाई होती है ( xऔर किसी भी मामले में कभी भी इसकी पुनरावृत्ति नहीं होती है, इसलिए रन टाइम पर कोई प्रभाव नहीं पड़ता है, यही कारण है कि लंबी सूची को थोड़े समय के लिए, दूसरे तरीके की तुलना में बेहतर बनाना है) लेकिन स्पर्शोन्मुख जटिलता पूरी कहानी नहीं बताती है। iterates और appends , फिर iterates और का परिणाम बताता है । और दोनों एक बार पुन: प्रसारित होते हैं। दोहराता और संलग्न कर देता है , तो का परिणाम iterates और संलग्न कर देता है । अभी भी एक बार iterated है, लेकिन इस मामले में दो बार iterated है। yzx ::: (y ::: z)yzxy ::: zxy(x ::: y) ::: zxyx ::: yzyx
पुहलेन

84

:::केवल सूचियों के साथ काम करता है, जबकि ++किसी भी ट्रैवर्सेबल के साथ उपयोग किया जा सकता है। वर्तमान कार्यान्वयन (2.9.0) में, तर्क ++वापस :::भी आ जाता है List


4
इसलिए सूची के साथ काम करने में ::: और ++ दोनों का उपयोग करना बहुत आसान है। वह संभावित रूप से कोड / शैली में गड़बड़ी कर सकता है।
Ses

24

एक अलग बिंदु यह है कि पहले वाक्य को इस प्रकार रखा गया है:

scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)

जबकि दूसरा उदाहरण इस प्रकार है:

scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)

इसलिए यदि आप मैक्रोज़ का उपयोग कर रहे हैं, तो आपको ध्यान रखना चाहिए।

इसके अलावा, ++दो सूचियों के लिए कॉल किया जा रहा है, :::लेकिन अधिक ओवरहेड के साथ क्योंकि यह एक अंतर्निहित मूल्य के लिए एक बिल्डर से सूची से सूची के लिए पूछ रहा है। लेकिन माइक्रोबेनचर्च उस अर्थ में कुछ भी उपयोगी साबित नहीं हुए, मुझे लगता है कि कंपाइलर इस तरह के कॉल का अनुकूलन करता है।

गर्म करने के बाद माइक्रो-बेंचमार्क।

scala>def time(a: => Unit): Long = { val t = System.currentTimeMillis; a; System.currentTimeMillis - t}
scala>def average(a: () => Long) = (for(i<-1 to 100) yield a()).sum/100

scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ++ List(e) } })
res1: Long = 46
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ::: List(e ) } })
res2: Long = 46

जैसा कि डैनियल सी। सोबराई ने कहा, आप किसी भी संग्रह की सामग्री का उपयोग करके किसी सूची में जोड़ सकते हैं ++, जबकि :::आप केवल सूची को संक्षिप्त कर सकते हैं।


20
कृपया अपने नहीं सरलीकृत microbenchमार्क्स पोस्ट करें और मैं उन्हें वोट दूंगा।
मिकाइल मेयर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.