2010-07-12 3 views
7

मान लीजिए कि मैं map जैसे स्कैला List पर कार्यक्षमता जोड़ना चाहता हूं, list mapmap f की रेखाओं के साथ कुछ, जो के प्रत्येक तत्व को f पर लागू करता है। (ए और अधिक गंभीर उदाहरण एक समानांतर या वितरित मानचित्र को लागू हो सकता है, लेकिन मैं उस दिशा में विवरण से विचलित करने के लिए नहीं करना चाहती।)क्या मैं ट्रैवर्सबल Like.map के एनालॉग के साथ "मेरी लाइब्रेरी को पंप कर सकता हूं" जिसमें अच्छी तरह से भिन्न प्रकार हैं?

मेरा पहला दृष्टिकोण

object MapMap { 
    implicit def createFancyList[A](list: List[A]) = new Object { 
     def mapmap(f: A => A): List[A] = { list map { a: A => f(f(a)) } } 
    } 
} 

होगा यह अब अच्छा काम करता है

scala> import MapMap._ 
import MapMap._ 

scala> List(1,2,3) mapmap { _ + 1 } 
res1: List[Int] = List(3, 4, 5) 

पाठ्यक्रम के अलावा यह केवल List रों के लिए है, और वहाँ एक map समारोह, जैसे के साथ कोई कारण नहीं है कि हम इस कुछ भी Traverseable के लिए काम नहीं करना चाहता चाहिए, Set एस या Stream एस। तो दूसरा प्रयास

object MapMap2 { 
    implicit def createFancyTraversable[A](t: Traversable[A]) = new Object { 
     def mapmap(f: A => A): Traversable[A] = { t map { a: A => f(f(a)) } } 
    } 
} 

तरह लग रहा है लेकिन अब, जाहिर है, परिणाम एक List[A] को सौंपा नहीं जा सकता है:

scala> import MapMap2._ 
import MapMap2._ 

scala> val r: List[Int] = List(1,2,3) mapmap { _ + 1 } 
<console>:9: error: type mismatch; 
found : Traversable[Int] 
required: List[Int] 

वहाँ कुछ बीच मैदान है? क्या मैं एक अंतर्निहित रूपांतरण लिख सकता हूं जो ट्रैवर्सबल के सभी उप-वर्गों में एक विधि जोड़ता है, और सफलतापूर्वक उस प्रकार के ऑब्जेक्ट्स लौटाता है?

(मुझे लगता है कि कर रहा हूँ इस खतरनाक CanBuildFrom विशेषता को समझने शामिल है, और भी हो सकता है breakout!)

उत्तर

10

आप सभी Traversables के लिए ऐसा नहीं कर सकते, वे डॉन के रूप में यह गारंटी नहीं है कि नक्शा ट्रैवर्सबल से कुछ और विशिष्ट लौटाता है। नीचे अपडेट 2 देखें।

import collection.generic.CanBuildFrom 
import collection.TraversableLike 

class TraversableW[CC[X] <: TraversableLike[X, CC[X]], A](value: CC[A]) { 
    def mapmap(f: A => A)(implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): CC[A] 
     = value.map(f andThen f) 
    def mapToString(implicit cbf: CanBuildFrom[CC[A], String, CC[String]]): CC[String] 
     = value.map(_.toString) 
} 

object TraversableW { 
    implicit def TraversableWTo[CC[X] <: TraversableLike[X, CC[X]], A](t: CC[A]): TraversableW[CC, A] 
     = new TraversableW[CC, A](t) 
} 

locally { 
    import TraversableW._ 

    List(1).mapmap(1+) 
    List(1).mapToString 
    // The static type of Seq is preserved, *and* the dynamic type of List is also 
    // preserved. 
    assert((List(1): Seq[Int]).mapmap(1+) == List(3)) 
} 

अद्यतन मैं प्रदर्शित करने के लिए क्यों TraversableW दो प्रकार पैरामीटर के बजाय एलेक्सी के समाधान के रूप में एक पैरामीटर स्वीकार करता है एक और pimped विधि, mapToString जोड़ दिया है,। पैरामीटर CC एक उच्च प्रकार का प्रकार है, यह मूल संग्रह के कंटेनर प्रकार का प्रतिनिधित्व करता है। दूसरा पैरामीटर, A, मूल संग्रह के तत्व प्रकार का प्रतिनिधित्व करता है। विधि mapToString इस प्रकार मूल कंटेनर प्रकार को एक अलग तत्व प्रकार के साथ वापस करने में सक्षम है: CC[String

अद्यतन 2 @oxbow_lakes टिप्पणी के लिए धन्यवाद, मैंने इसे फिर से विचार किया है। CC[X] <: Traversable[X], TraversableLike को सीधे पंप करना वास्तव में संभव है। टिप्पणियां इनलाइन:

import collection.generic.CanBuildFrom 
import collection.TraversableLike 

class TraversableW[CC[X] <: Traversable[X], A](value: CC[A]) { 
    /** 
    * A CanBuildFromInstance based purely the target element type `Elem` 
    * and the target container type `CC`. This can be converted to a 
    * `CanBuildFrom[Source, Elem, CC[Elem]` for any type `Source` by 
    * `collection.breakOut`. 
    */ 
    type CanBuildTo[Elem, CC[X]] = CanBuildFrom[Nothing, Elem, CC[Elem]] 

    /** 
    * `value` is _only_ known to be a `Traversable[A]`. This in turn 
    * turn extends `TraversableLike[A, Traversable[A]]`. The signature 
    * of `TraversableLike#map` requires an implicit `CanBuildFrom[Traversable[A], B, That]`, 
    * specifically in the call below `CanBuildFrom[Traversable[A], A CC[A]`. 
    * 
    * Essentially, the specific type of the source collection is not known in the signature 
    * of `map`. 
    * 
    * This cannot be directly found instead we look up a `CanBuildTo[A, CC[A]]` and 
    * convert it with `collection.breakOut` 
    * 
    * In the first example that referenced `TraversableLike[A, CC[A]]`, `map` required a 
    * `CanBuildFrom[CC[A], A, CC[A]]` which could be found. 
    */ 
    def mapmap(f: A => A)(implicit cbf: CanBuildTo[A, CC]): CC[A] 
     = value.map[A, CC[A]](f andThen f)(collection.breakOut) 
    def mapToString(implicit cbf: CanBuildTo[String, CC]): CC[String] 
     = value.map[String, CC[String]](_.toString)(collection.breakOut) 
} 

object TraversableW { 
    implicit def TraversableWTo[CC[X] <: Traversable[X], A](t: CC[A]): TraversableW[CC, A] 
     = new TraversableW[CC, A](t) 
} 

locally { 
    import TraversableW._ 

    assert((List(1)).mapmap(1+) == List(3)) 

    // The static type of `Seq` has been preserved, but the dynamic type of `List` was lost. 
    // This is a penalty for using `collection.breakOut`. 
    assert((List(1): Seq[Int]).mapmap(1+) == Seq(3)) 
} 

क्या अंतर है?हमें collection.breakOut का उपयोग करना पड़ा, क्योंकि हम केवल Traversable[A] से विशिष्ट संग्रह उप प्रकार को पुनर्प्राप्त नहीं कर सकते थे।

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { 
    val b = bf(repr) 
    b.sizeHint(this) 
    for (x <- this) b += f(x) 
    b.result 
} 

Builderb मूल संग्रह है, जो तंत्र एक map के माध्यम से गतिशील प्रकार संरक्षित करने के लिए है के साथ आरंभ नहीं हो जाता। हालांकि, हमारे CanBuildFrom ने से सभी तर्कों को अस्वीकार कर दिया, प्रकार तर्क Nothing के प्रकार से।

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() 
    } 

हम b.apply(from), कोई और अधिक फोन नहीं कर सकते हैं की तुलना में आप def foo(a: Nothing) = 0 कह सकते हैं: तुम सब Nothing साथ कर सकते हैं उस पर ध्यान नहीं है, जो है कि वास्तव में क्या breakOut करता है।

+0

पूरी तरह से काम करता है! –

+2

मैंने स्कालाज़ में थोड़ा अलग दृष्टिकोण लिया, जो थोड़ा अधिक शक्तिशाली है: http://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/CanBuildAnySelf.scala#L24 http: //github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Functor.scala#L28 – retronym

+0

मेरे पास इसे संपादित करने का अधिकार नहीं है, संक्षेप में, लेकिन शायद कोड का स्वरूपित आपके उदाहरण में आयात को शामिल करने के लिए ब्लॉक को बढ़ाया जाना चाहिए? चीयर्स! – pr1001

5

एक सामान्य नियम है, जब आप एक ही प्रकार के साथ वस्तुओं लौटना चाहते रूप में, आप TraversableLike (IterableLike, SeqLike की जरूरत , आदि) Traversable के बजाय। यहाँ सबसे सामान्य संस्करण मैं के साथ आ सकता है (अलग FancyTraversable वर्ग संरचनात्मक प्रकार और प्रतिबिंब हिट का निष्कर्ष निकालते से बचने के लिए नहीं है):

class FancyTraversable[A, S <: TraversableLike[A, S]](t: S) { 
    def mapmap(f: A => A)(implicit bf: CanBuildFrom[S,A,S]): S = { t map { a: A => f(f(a)) } } 
} 

implicit def createFancyTraversable[A, S <: TraversableLike[A, S]](t: S): FancyTraversable[A, S] = new FancyTraversable(t) 
+0

क्या मुझे कुछ आयात करना है? मुझे एक "त्रुटि मिल रही है: नहीं मिला: ट्रैवर्सबल लाइक टाइप करें"। (2.8.0 आरसी 7) –

+0

"आयात scala.collection._" और "आयात scala.collection.generic._" कम से कम इसे संकलित करता है, लेकिन अब "सूची (1,2,3) मैपमैप {_ + 1}" बस मुझे "त्रुटि देता है: मान मैपमैप सूची [Int] का सदस्य नहीं है"। –

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

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