2010-10-09 7 views
10

मैं इतनी दूर तक पहुँच चुके हैं:एक ज़िप विधि कैसे लिखें जो उसी प्रकार के संग्रह को लौटाता है जैसा कि इसे पास किया गया है?

implicit def collectionExtras[A](xs: Iterable[A]) = new { 
    def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Iterable[A], C, That]) = { 
    val builder = cbf(xs.repr) 
    val (i, j) = (xs.iterator, ys.iterator) 
    while(i.hasNext && j.hasNext) { 
     builder += f(i.next, j.next) 
    } 
    builder.result 
    } 
} 
// collectionExtras: [A](xs: Iterable[A])java.lang.Object{def zipWith[B,C,That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: scala.collection.generic.CanBuildFrom[Iterable[A],C,That]): That} 

Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _) 
// res3: Iterable[Int] = Vector(8, 8, 8) 

अब समस्या यह है कि विधि ऊपर हमेशा एक Iterable देता है। मैं इसे किस तरह से पास किए गए संग्रह के संग्रह को वापस कर सकता हूं? (इस मामले में, Vector) धन्यवाद।

+0

'new {def foo =} 'संरचनात्मक प्रकार का मान उत्पन्न करता है, जिसे प्रतिबिंब के माध्यम से बुलाया जाता है; इससे बचने के लिए, एक विशेषता ZipWith में हस्ताक्षर घोषित करें, और इस विशेषता का एक उदाहरण वापस करें। यह प्रश्न और सभी समाधानों पर लागू होता है। – Blaisorblade

उत्तर

9

आप काफी करीब आ गए। बस दो पंक्तियों में एक मामूली परिवर्तन:

implicit def collectionExtras[A, CC[A] <: IterableLike[A, CC[A]]](xs: CC[A]) = new { 
    def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[CC[A], C, That]) = { 
    val builder = cbf(xs.repr) 
    val (i, j) = (xs.iterator, ys.iterator) 
    while(i.hasNext && j.hasNext) { 
     builder += f(i.next, j.next) 
    } 
    builder.result 
    } 
} 

सबसे पहले, आप संग्रह प्रकार पारित किया जा रहा प्राप्त करने की आवश्यकता है, इसलिए मैं एक प्रकार पैरामीटर के रूप में CC[A] गयी। साथ ही, उस संग्रह को खुद को "पुन: उत्पन्न" करने में सक्षम होना चाहिए - यह IterableLike के दूसरे प्रकार के पैरामीटर द्वारा गारंटीकृत है - इसलिए CC[A] <: IterableLike[A, CC[A]]। ध्यान दें कि IterableLike का यह दूसरा पैरामीटर Repr है, ठीक प्रकार xs.repr का प्रकार।

स्वाभाविक रूप से, CanBuildFrom को Iterable[A] के बजाय CC[A] प्राप्त करने की आवश्यकता है। और यह सब कुछ है।

और परिणाम:

scala> Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _) 
res0: scala.collection.immutable.Vector[Int] = Vector(8, 8, 8) 
+0

'हैनक्स्ट' पर गिर रहा है और इसका मतलब है कि यह अनंत स्ट्रीम पर काम नहीं करता है। huynhjl का समाधान करता है। –

+0

मेरे पास आपके उत्तर के साथ दो समस्याएं हैं: ए) आप 'सीसी [ए]' लिखते हैं, जो सूचियों को ज़िप करते समय समस्याग्रस्त है (एक कारण है कि स्कैला संग्रह लाइब्रेरी इस तरह उच्च रैंकिंग प्रकारों का उपयोग नहीं करती है, और मुझे यकीन है तुम्हे पता हैं)। बी) इसके अलावा यह उत्तर 'संग्रह एक्स्ट्रा' रिटर्न प्रकार को संरचनात्मक प्रकार (और इस प्रकार अक्षम) बनाता है। – Blaisorblade

+0

@Blaisorblade यदि आप axel22 पर मेरी टिप्पणी देखते हैं, उस समय अन्य समाधान मेरे साथ नहीं हुआ था। संरचनात्मक प्रकार के लिए, मुझे नहीं लगता कि यह कैसे होता है - संरचनात्मक प्रकार इस तरह से मुश्किल हो सकते हैं। : -/ –

8

समस्या से ऊपर है कि आपके निहित रूपांतरण collectionExtras प्राप्त ऑब्जेक्ट प्रकार जानकारी खोने के लिए कारण बनता है। विशेष रूप से, उपर्युक्त समाधान में, कंक्रीट संग्रह प्रकार खो जाता है क्योंकि आप इसे Iterable[A] प्रकार का ऑब्जेक्ट पास कर रहे हैं - इस बिंदु से, कंपाइलर अब xs के वास्तविक प्रकार को नहीं जानता है। हालांकि बिल्डर फैक्टरी CanBuildFrom प्रोग्रामेटिक रूप से सुनिश्चित करता है कि संग्रह का गतिशील प्रकार सही है (आपको वास्तव में Vector मिलता है), स्थिर रूप से, संकलक केवल zipWith कुछ Iterable देता है।

इस समस्या को हल करने के लिए, अंतर्निहित रूपांतरण होने के बजाय Iterable[A] लें, इसे IterableLike[A, Repr] लें। क्यूं कर?

Iterable[A] extends IterableLike[A, Iterable[A]] 

Iterable साथ अंतर यह है कि इस IterableLike[A, Repr]Repr के रूप में ठोस संग्रह प्रकार रहता है:

Iterable[A] आमतौर पर की तरह कुछ के रूप में घोषित किया गया है। सबसे ठोस संग्रह, Iterable[A] में मिश्रण के अलावा, यह भी विशेषता IterableLike[A, Repr] में मिश्रण, नीचे की तरह, उनके ठोस प्रकार के साथ Repr की जगह:

Vector[A] extends Iterable[A] with IterableLike[A, Vector[A]] 

वे क्या कर सकते हैं इस वजह प्रकार पैरामीटर Repr covariant के रूप में घोषित किया गया है।

लंबी कहानी संक्षेप में, IterableLike कारणों का उपयोग कर आप ठोस संग्रह प्रकार की जानकारी रखने के (कि Repr है) के आसपास है और इसका इस्तेमाल करते हैं जब आप zipWith परिभाषित करने के लिए अंतर्निहित रूपांतरण - ध्यान दें कि बिल्डर कारखाने CanBuildFrom अब के लिए ReprIterable[A] के बजाय शामिल होंगे पहले प्रकार पैरामीटर, उचित अंतर्निहित वस्तु के कारण हल किया जाना:

import collection._ 
import collection.generic._ 

implicit def collectionExtras[A, Repr](xs: IterableLike[A, Repr]) = new { 
    def zipWith[B, C, That](ys: Iterable[B])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Repr, C, That]) = { 
    val builder = cbf(xs.repr) 
    val (i, j) = (xs.iterator, ys.iterator) 
    while(i.hasNext && j.hasNext) { 
     builder += f(i.next, j.next) 
    } 
    builder.result 
    } 
} 

अपने प्रश्न तैयार करने और अधिक ध्यान से पढ़ना ("? कैसे एक zipWith विधि है कि यह करने के लिए पारित किया उन लोगों के रूप संग्रह के एक ही प्रकार रिटर्न लिखने के लिए"), ऐसा लगता है कि आप एक ही प्रकार के संग्रह चाहते हैं आयन के रूप में zipWith पास हो गया, निहित रूपांतरण के लिए, यह ys जैसा ही प्रकार है।

import collection._ 
import collection.generic._ 

implicit def collectionExtras[A](xs: Iterable[A]) = new { 
    def zipWith[B, C, That, Repr](ys: IterableLike[B, Repr])(f: (A, B) => C)(implicit cbf: CanBuildFrom[Repr, C, That]) = { 
    val builder = cbf(ys.repr) 
    val (i, j) = (xs.iterator, ys.iterator) 
    while(i.hasNext && j.hasNext) { 
     builder += f(i.next, j.next) 
    } 
    builder.result 
    } 
} 
परिणामों के साथ

:

scala> immutable.Vector(2, 2, 2).zipWith(mutable.ArrayBuffer(4, 4, 4))(_ * _) 
res1: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 8, 8) 
+1

दिलचस्प। 'IterableLike [_, Repr]' प्राप्त करने के लिए यह कभी नहीं हुआ, और 'Repr' पर पैरामीटर। –

+0

मुझे लगता है कि 'रेप्र' एक 'स्ट्रिंग' है, या कुछ अन्य अनैमेट्रिज्ड प्रकार, जैसे 'स्ट्रिंगऑप्स' में एक फायदा हो सकता है। – axel22

+0

'हैनक्स्ट' पर गिर रहा है और इसका मतलब है कि यह अनंत स्ट्रीम पर काम नहीं करता है। huynhjl का समाधान करता है। –

5

ईमानदारी से कहूं तो कैसे कि वास्तव में काम करता है मैं सुनिश्चित नहीं हूं: पहले की तरह

एक ही कारण, समाधान नीचे देखें

implicit def collectionExtras[CC[X] <: Iterable[X], A](xs: CC[A]) = new { 
    import collection.generic.CanBuildFrom 
    def zipWith[B, C](ys: Iterable[B])(f: (A, B) => C) 
    (implicit cbf:CanBuildFrom[Nothing, C, CC[C]]): CC[C] = { 
    xs.zip(ys).map(f.tupled)(collection.breakOut) 
    } 
} 

scala> Vector(2, 2, 2).zipWith(Vector(4, 4, 4))(_ * _) 
res1: scala.collection.immutable.Vector[Int] = Vector(8, 8, 8) 

मैं बंदर की तरह this answer from retronym पैच किया जब तक यह काम नहीं किया!

असल में, मैं संकेत मिलता है कि zipWithxs का संग्रह प्रकार लौटना चाहिए लेकिन C प्रकार पैरामीटर (CC[C]) के रूप में साथ CC[X] प्रकार निर्माता का उपयोग करना चाहते। और मैं सही परिणाम प्रकार प्राप्त करने के लिए breakOut का उपयोग करना चाहता हूं। मैं इन सबसे छुटकारा आशा व्यक्त की एक CanBuildFrom दायरे में निहित था, लेकिन फिर यह त्रुटि संदेश मिल गया कि:

required: scala.collection.generic.CanBuildFrom[Iterable[(A, B)],C,CC[C]] 

चाल तो था Nothing बजाय Iterable[(A, B)] उपयोग करने के लिए। मुझे लगता है कि निहित कहीं परिभाषित किया गया है ...

इसके अलावा, मुझे zipWithzip और फिर map के रूप में सोचना पसंद है, इसलिए मैंने कार्यान्वयन को बदल दिया। यहाँ अपने कार्यान्वयन के साथ है:

implicit def collectionExtras[CC[X] <: Iterable[X], A](xs: CC[A]) = new { 
    import collection.generic.CanBuildFrom 
    def zipWith[B, C](ys: Iterable[B])(f: (A, B) => C) 
    (implicit cbf:CanBuildFrom[Nothing, C, CC[C]]) : CC[C] = { 
    val builder = cbf() 
    val (i, j) = (xs.iterator, ys.iterator) 
    while(i.hasNext && j.hasNext) { 
     builder += f(i.next, j.next) 
    } 
    builder.result 
    } 
} 

नोट this article प्रकार निर्माता तर्ज पर कुछ पृष्ठभूमि प्रदान करता है।

+0

यदि आप इटरेटर स्तर पर जाने के बजाए ज़िप और मानचित्र का उपयोग करते हैं, तो यह अनंत स्ट्रीम के साथ काम करता है। +1! –

+0

स्कैला 2.10 में 'निहित वर्ग' के साथ काम करता है! http://gist.github.com/2942990 देखें –

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