स्कैला 2.8 ब्रेकऑउट


225

2.8 स्केल में , इसमें एक वस्तु है scala.collection.package.scala:

def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
    new CanBuildFrom[From, T, To] {
        def apply(from: From) = b.apply() ; def apply() = b.apply()
 }

मुझे बताया गया है कि यह परिणाम है:

> import scala.collection.breakOut
> val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)

map: Map[Int,String] = Map(6 -> London, 5 -> Paris)

यहाँ क्या हो रहा है? मेरे लिए एक तर्क के रूप में क्यों breakOutकहा जा रहा है ?List


13
तुच्छ उत्तर होने के नाते, यह एक तर्क नहीं है List, लेकिन करने के लिए है map
डैनियल सी। सोबरल

जवाबों:


325

इसका उत्तर निम्नलिखित की परिभाषा पर मिलता है map:

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

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

के बारे में breakOut

तो, इसका उद्देश्य क्या है breakOut? प्रश्न के लिए दिए गए उदाहरण पर विचार करें, आप स्ट्रिंग की एक सूची लेते हैं, प्रत्येक स्ट्रिंग को टुपल में बदलते हैं (Int, String), और फिर Mapउसमें से एक का उत्पादन करते हैं। सबसे स्पष्ट तरीका यह है कि एक मध्यस्थ List[(Int, String)]संग्रह का उत्पादन होगा , और फिर इसे परिवर्तित करें।

यह देखते हुए कि परिणामी संग्रह का उत्पादन करने के लिए mapएक Builderका उपयोग करता है , क्या यह मध्यस्थ को छोड़ना Listऔर परिणामों को सीधे एक में इकट्ठा करना संभव नहीं होगा Map? जाहिर है, हाँ, यह है। हालांकि, ऐसा करने के लिए, हमें एक उचित पास करने की आवश्यकता होती CanBuildFromहै map, और ठीक वैसा ही breakOutहोता है।

आइए, फिर देखें breakOut: की परिभाषा पर :

def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
  new CanBuildFrom[From, T, To] {
    def apply(from: From) = b.apply() ; def apply() = b.apply()
  }

ध्यान दें कि breakOutपैरामीटर किया गया है, और यह कि इसका एक उदाहरण देता है CanBuildFrom। जैसा कि होता है, प्रकार From, Tऔर Toपहले से ही अनुमान लगाया गया है, क्योंकि हम जानते हैं कि mapउम्मीद है CanBuildFrom[List[String], (Int, String), Map[Int, String]]। इसलिए:

From = List[String]
T = (Int, String)
To = Map[Int, String]

यह निष्कर्ष निकालने के लिए कि breakOutस्वयं द्वारा प्राप्त निहितार्थ की जांच करें । यह प्रकार का होता है CanBuildFrom[Nothing,T,To]। हम पहले से ही इन सभी प्रकारों को जानते हैं, इसलिए हम यह निर्धारित कर सकते हैं कि हमें प्रकार के निहितार्थ की आवश्यकता है CanBuildFrom[Nothing,(Int,String),Map[Int,String]]। लेकिन क्या ऐसी कोई परिभाषा है?

आइए नजर डालते हैं CanBuildFromपरिभाषा:

trait CanBuildFrom[-From, -Elem, +To] 
extends AnyRef

तो CanBuildFromइसके पहले प्रकार के पैरामीटर पर गर्भ-संस्करण है। क्योंकि Nothingएक निचला वर्ग है (यानी, यह सब कुछ का एक उपवर्ग है), जिसका अर्थ है कि किसी भी वर्ग का उपयोग किया जा सकता है Nothing

चूंकि इस तरह के एक बिल्डर मौजूद है, स्काला इसका उपयोग वांछित उत्पादन करने के लिए कर सकती है।

बिल्डर्स के बारे में

स्काला के संग्रह पुस्तकालय के बहुत सारे तरीकों में मूल संग्रह लेना, इसे किसी तरह संसाधित करना ( mapप्रत्येक तत्व को बदलना), और परिणाम को एक नए संग्रह में संग्रहीत करना शामिल है।

कोड पुन: उपयोग को अधिकतम करने के लिए, परिणामों का यह भंडारण एक बिल्डर ( scala.collection.mutable.Builder) के माध्यम से किया जाता है , जो मूल रूप से दो कार्यों का समर्थन करता है: तत्वों को जोड़ना, और परिणामी संग्रह को वापस करना। इस परिणामी संग्रह का प्रकार बिल्डर के प्रकार पर निर्भर करेगा। इस प्रकार, एक Listबिल्डर वापस आ जाएगा List, एक Mapबिल्डर वापस आ जाएगा Map, और इसी तरह। mapविधि के कार्यान्वयन के परिणाम के प्रकार के साथ खुद को चिंता करने की आवश्यकता नहीं है: बिल्डर इसका ख्याल रखता है।

दूसरी ओर, इसका मतलब है कि mapकिसी भी तरह इस बिल्डर को प्राप्त करने की आवश्यकता है। स्केल 2.8 कलेक्शंस को डिजाइन करते समय समस्या का सामना करना पड़ा कि कैसे सबसे अच्छा संभव बिल्डर चुनना था। उदाहरण के लिए, अगर मैं लिखना Map('a' -> 1).map(_.swap)चाहता था , तो मैं Map(1 -> 'a')वापस जाना चाहता हूं । दूसरी ओर, यह ( Map('a' -> 1).map(_._1)ए ) वापस नहीं लौट सकता है ।MapIterable

Builderज्ञात प्रकार के अभिव्यक्ति से सर्वोत्तम संभव उत्पादन का जादू इस CanBuildFromनिहितार्थ के माध्यम से किया जाता है ।

के बारे में CanBuildFrom

यह समझने के लिए कि क्या चल रहा है, मैं एक उदाहरण दूंगा जहां संग्रहित किया जा रहा है, Mapबजाय एक है List। मैं Listबाद में वापस जाऊंगा । अभी के लिए, इन दो भावों पर विचार करें:

Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)

पहला रिटर्न ए Mapऔर दूसरा रिटर्न ए Iterable। फिटिंग कलेक्शन लौटाने का जादू है CanBuildFrom। आइए mapइसे समझने के लिए फिर से परिभाषा पर विचार करें ।

विधि mapसे विरासत में मिली है TraversableLike। यह चालू है Bऔर That, और प्रकार के मापदंडों का उपयोग करता है Aऔर Repr, जो वर्ग को मानकीकृत करता है। आइए दोनों परिभाषाओं को एक साथ देखें:

वर्ग TraversableLikeके रूप में परिभाषित किया गया है:

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

यह समझने के लिए कि कहां Aऔर कहां Reprसे आया है, आइए Mapस्वयं की परिभाषा पर विचार करें :

trait Map[A, +B] 
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

क्योंकि TraversableLikeउन सभी लक्षणों को विरासत में मिला है जो विस्तार करते हैं Map, Aऔर Reprउनमें से किसी से विरासत में प्राप्त किया जा सकता है। पिछले एक को वरीयता मिलती है, हालांकि। इसलिए, अपरिवर्तनीय Mapऔर इसे जोड़ने वाले सभी लक्षणों की परिभाषा के बाद TraversableLike, हमारे पास है:

trait Map[A, +B] 
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] 
extends MapLike[A, B, This]

trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] 
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]

trait IterableLike[+A, +Repr] 
extends Equals with TraversableLike[A, Repr]

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

यदि आप Map[Int, String]श्रृंखला के सभी प्रकारों के प्रकार के मापदंडों को पास करते हैं , तो हम पाते हैं कि प्रकार पास किए गए हैं TraversableLike, और इस प्रकार, द्वारा उपयोग किया जाता है map:

A = (Int,String)
Repr = Map[Int, String]

उदाहरण पर वापस जाने पर, पहला मानचित्र प्रकार का एक फ़ंक्शन प्राप्त कर रहा है ((Int, String)) => (Int, Int)और दूसरा मानचित्र प्रकार का फ़ंक्शन प्राप्त कर रहा है ((Int, String)) => String। मैं डबल कोष्ठक का उपयोग करने पर जोर देता हूं यह एक टपल प्राप्त किया जा रहा है, जैसा कि Aहमने देखा था उसी प्रकार का है ।

उस जानकारी के साथ, आइए अन्य प्रकारों पर विचार करें।

map Function.tupled(_ -> _.length):
B = (Int, Int)

map (_._2):
B = String

हम देख सकते हैं कि प्रकार पहले mapसे लौटा है Map[Int,Int], और दूसरा है Iterable[String]। को देखते हुए mapकी परिभाषा यह है कि इन के मूल्यों को देखने के लिए आसान है That। लेकिन वे कहां से आते हैं?

यदि हम शामिल कक्षाओं की साथी वस्तुओं के अंदर देखते हैं, तो हम उन्हें प्रदान करने वाले कुछ निहित घोषणाओं को देखते हैं। वस्तु पर Map:

implicit def  canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]  

और वस्तु पर Iterable, जिसका वर्ग इसके द्वारा बढ़ाया गया है Map:

implicit def  canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]  

ये परिभाषाएँ मानकीकृत के लिए कारखानों को प्रदान करती हैं CanBuildFrom

Scala उपलब्ध सबसे विशिष्ट निहित का चयन करेगा। पहले मामले में, यह पहला था CanBuildFrom। दूसरे मामले में, जैसा कि पहले मैच नहीं हुआ, उसने दूसरे को चुना CanBuildFrom

वापस प्रश्न पर

चलो प्रश्न के लिए कोड को देखते हैं, List'और एस mapएस डेफिनिशन (फिर) को देखने के लिए कैसे प्रकार पर आधारित हैं:

val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)

sealed abstract class List[+A] 
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]

trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] 
extends SeqLike[A, Repr]

trait SeqLike[+A, +Repr] 
extends IterableLike[A, Repr]

trait IterableLike[+A, +Repr] 
extends Equals with TraversableLike[A, Repr]

trait TraversableLike[+A, +Repr] 
extends HasNewBuilder[A, Repr] with AnyRef

def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That 

का प्रकार List("London", "Paris")है List[String], इसलिए प्रकार Aऔर Reprपरिभाषित TraversableLikeहैं:

A = String
Repr = List[String]

के लिए प्रकार (x => (x.length, x))है (String) => (Int, String), इसलिए प्रकार Bहै:

B = (Int, String)

अंतिम अज्ञात प्रकार, Thatके परिणाम का प्रकार है map, और हमारे पास पहले से ही है:

val map : Map[Int,String] =

इसलिए,

That = Map[Int, String]

इसका मतलब है breakOut, आवश्यक रूप से, का एक प्रकार या उपप्रकार वापस करना चाहिए CanBuildFrom[List[String], (Int, String), Map[Int, String]]


61
डैनियल, मैं आपके उत्तर के प्रकारों के माध्यम से कमर कस सकता हूं, लेकिन एक बार जब मैं अंत तक पहुंच जाता हूं, तो मुझे लगता है कि मैंने कोई उच्च स्तरीय समझ हासिल नहीं की है। क्या है ब्रेकआउट? "ब्रेकऑट" नाम कहां से आता है (मैं क्या तोड़ रहा हूं)? मैप से बाहर निकलने के लिए इस मामले में इसकी आवश्यकता क्यों है? निश्चित रूप से इन सवालों का संक्षिप्त जवाब देने का कोई तरीका है? (यहां तक ​​कि अगर हर विवरण को समझने के लिए लंबा-चौड़ा प्रकार आवश्यक है)
सेठ टिस्यू

3
@Seth यह एक वैध चिंता है, लेकिन मुझे यकीन नहीं है कि मैं इस कार्य के लिए तैयार हूं। इसका मूल यहाँ पाया जा सकता है: article.gmane.org/gmane.comp.lang.scala.internals/1812/… । मैं इसके बारे में सोचूंगा, लेकिन, अभी, मैं इसे सुधारने का एक तरीका नहीं सोच सकता।
डैनियल सी। सोबरल

2
क्या मानचित्र [Int, String] के पूरे परिणाम प्रकार को निर्दिष्ट करने से बचने का एक तरीका है और इसके बजाय कुछ लिखने में सक्षम है: 'val map = List ("London", "Paris")। map (x => (x) लंबाई, x)) (ब्रेकऑट [... मैप]) '
इट्टायड

9
@SethTisue मेरे इस स्पष्टीकरण को पढ़ने से, ऐसा लगता है कि ब्रेकऑउट आवश्यक है कि "बिल्डर को बाहर निकालना" आवश्यक है जिसे आपके बिल्डर को एक सूची [स्ट्रिंग] से बनाने की आवश्यकता है। संकलक एक CanBuildFrom [सूची [स्ट्रिंग], (इंट, स्ट्रिंग), मानचित्र [इंट, स्ट्रिंग]] चाहता है, जिसे आप प्रदान नहीं कर सकते। ब्रेकऑउट फ़ंक्शन इसे कुछ भी करने के लिए सेट करके CanBuildFrom में पहले प्रकार के पैरामीटर को क्लोब करके करता है। अब आपको केवल CanBuildFrom [कुछ भी नहीं, (Int, String), Map [Int, String]] प्रदान करना होगा। यह आसान है क्योंकि यह मानचित्र वर्ग द्वारा प्रदान किया गया है।
मार्क

2
@ मर्क जब मुझे ब्रेकऑउट मिला, तो मैंने जो समस्या देखी, वह यह थी कि मोनड्स अपने स्वयं के प्रकार के लिए मैपिंग (बाइंड / फ़्लैटअप के माध्यम से) पर जोर देते हैं। यह एक एक मैपिंग श्रृंखला के "ब्रेक आउट" को एक अलग मोनाड प्रकार में एक मोनड का उपयोग करने की अनुमति देता है। मुझे पता नहीं है कि एड्रियन मूर (लेखक) इसके बारे में क्या सोच रहा था, हालांकि!
एड स्टब

86

मैं डैनियल के जवाब पर निर्माण करना चाहता हूं। यह बहुत गहन था, लेकिन जैसा कि टिप्पणियों में उल्लेख किया गया है, यह स्पष्ट नहीं करता है कि ब्रेकआउट क्या करता है।

फिर से लिया गया : स्पष्ट बिल्डर्स (2009-10-23) के लिए समर्थन , यहाँ मेरा मानना ​​है कि ब्रेकआउट क्या करता है:

यह कंपाइलर को एक सुझाव देता है कि बिल्डर को कौन सा चयन करना है (अनिवार्य रूप से यह कंपाइलर को यह चुनने की अनुमति देता है कि वह किस फैक्ट्री को लगता है कि स्थिति सबसे अच्छी है।)

उदाहरण के लिए, निम्नलिखित देखें:

scala> import scala.collection.generic._
import scala.collection.generic._

scala> import scala.collection._
import scala.collection._

scala> import scala.collection.mutable._
import scala.collection.mutable._

scala>

scala> def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
     |    new CanBuildFrom[From, T, To] {
     |       def apply(from: From) = b.apply() ; def apply() = b.apply()
     |    }
breakOut: [From, T, To]
     |    (implicit b: scala.collection.generic.CanBuildFrom[Nothing,T,To])
     |    java.lang.Object with
     |    scala.collection.generic.CanBuildFrom[From,T,To]

scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)

scala> val imp = l.map(_ + 1)(breakOut)
imp: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4)

scala> val arr: Array[Int] = l.map(_ + 1)(breakOut)
imp: Array[Int] = Array(2, 3, 4)

scala> val stream: Stream[Int] = l.map(_ + 1)(breakOut)
stream: Stream[Int] = Stream(2, ?)

scala> val seq: Seq[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Seq[Int] = ArrayBuffer(2, 3, 4)

scala> val set: Set[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.Set[Int] = Set(2, 4, 3)

scala> val hashSet: HashSet[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.HashSet[Int] = Set(2, 4, 3)

आप देख सकते हैं कि संकलक द्वारा अपेक्षित प्रकार से सर्वश्रेष्ठ मिलान करने के लिए रिटर्न प्रकार संकलक द्वारा चुना गया है। आप प्राप्त चर को कैसे घोषित करते हैं, इसके आधार पर आपको अलग-अलग परिणाम मिलते हैं।

एक बिल्डर को निर्दिष्ट करने के लिए निम्नलिखित एक समान तरीका होगा। इस मामले में ध्यान दें, कंपाइलर बिल्डर के प्रकार के आधार पर अपेक्षित प्रकार का अनुमान लगाएगा:

scala> def buildWith[From, T, To](b : Builder[T, To]) =
     |    new CanBuildFrom[From, T, To] {
     |      def apply(from: From) = b ; def apply() = b
     |    }
buildWith: [From, T, To]
     |    (b: scala.collection.mutable.Builder[T,To])
     |    java.lang.Object with
     |    scala.collection.generic.CanBuildFrom[From,T,To]

scala> val a = l.map(_ + 1)(buildWith(Array.newBuilder[Int]))
a: Array[Int] = Array(2, 3, 4)

1
मुझे आश्चर्य है कि इसका नाम " breakOut" क्यों है ? मैं कुछ ऐसा सोच रहा हूं convertया buildADifferentTypeOfCollection(लेकिन कम) याद रखना आसान हो सकता है।
काजमग्नस

8

डैनियल सोबरल का जवाब बहुत अच्छा है, और आर्किटेक्चर ऑफ स्काला कलेक्शंस (स्कैला में प्रोग्रामिंग के अध्याय 25) के साथ एक साथ पढ़ा जाना चाहिए ।

मैं सिर्फ इस बात को विस्तृत करना चाहता हूं कि इसे क्यों कहा जाता है breakOut:

इसे क्यों कहा जाता है breakOut?

क्योंकि हम एक प्रकार से और दूसरे में टूटना चाहते हैं :

किस प्रकार में से बाहर तोड़? एक उदाहरण के रूप में mapकार्य को देखते हैं Seq:

Seq.map[B, That](f: (A) -> B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That

अगर हम किसी अनुक्रम के तत्वों पर मानचित्रण से सीधे मानचित्र बनाना चाहते हैं जैसे:

val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))

कंपाइलर शिकायत करेगा:

error: type mismatch;
found   : Seq[(String, Int)]
required: Map[String,Int]

इसका कारण यह है कि Seq केवल एक और Seq का निर्माण करना जानता है (यानी एक अंतर्निहित CanBuildFrom[Seq[_], B, Seq[B]]बिल्डर कारखाना उपलब्ध है, लेकिन Seq से लेकर Map तक कोई बिल्डर कारखाना नहीं है )।

संकलन करने के लिए, हमें किसी breakOutप्रकार की आवश्यकता है , और एक बिल्डर का निर्माण करने में सक्षम होना चाहिए जो mapफ़ंक्शन का उपयोग करने के लिए एक मानचित्र बनाता है ।

जैसा कि डैनियल ने समझाया है, ब्रेकऑउट में निम्नलिखित हस्ताक्षर हैं:

def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To] =
    // can't just return b because the argument to apply could be cast to From in b
    new CanBuildFrom[From, T, To] {
      def apply(from: From) = b.apply()
      def apply()           = b.apply()
    }

Nothingसभी वर्गों का एक उपवर्ग है, इसलिए किसी भी बिल्डर कारखाने को इसके स्थान पर प्रतिस्थापित किया जा सकता है implicit b: CanBuildFrom[Nothing, T, To]। यदि हम अंतर्निहित पैरामीटर प्रदान करने के लिए ब्रेकऑट फ़ंक्शन का उपयोग करते हैं:

val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)

यह संकलित करेगा, क्योंकि breakOutआवश्यक प्रकार प्रदान करने में सक्षम है CanBuildFrom[Seq[(String, Int)], (String, Int), Map[String, Int]], जबकि संकलक वास्तविक बिल्डर बनाने के लिए उपयोग करने के लिए ब्रेकऑउट के CanBuildFrom[Map[_, _], (A, B), Map[A, B]]स्थान पर CanBuildFrom[Nothing, T, To], प्रकार के एक अंतर्निहित बिल्डर कारखाने को खोजने में सक्षम है ।

ध्यान दें कि CanBuildFrom[Map[_, _], (A, B), Map[A, B]]मानचित्र में परिभाषित किया गया है, और बस एक आरंभिक MapBuilderमानचित्र का उपयोग करता है।

आशा है, इससे स्थिति स्पष्ट हो जाएगी।


4

समझने के लिए एक सरल उदाहरण क्या breakOutहै:

scala> import collection.breakOut
import collection.breakOut

scala> val set = Set(1, 2, 3, 4)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

scala> set.map(_ % 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 0)

scala> val seq:Seq[Int] = set.map(_ % 2)(breakOut)
seq: Seq[Int] = Vector(1, 0, 1, 0) // map created a Seq[Int] instead of the default Set[Int]

उदाहरण के लिए धन्यवाद! इसके अलावा val seq:Seq[Int] = set.map(_ % 2).toVectorआपको बार-बार मान नहीं देगा क्योंकि Setसंरक्षित किया गया था map
मैथ्यू पिकरिंग

@ मैथेकपिकिंग सही! पहले set.map(_ % 2)बनाता है Set(1, 0), जो फिर एक में परिवर्तित हो जाता है Vector(1, 0)
fdietze
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.