2010-04-02 4 views
24

का उपयोग करके सूची के विकल्प में विकल्पों की एक सूची में कनवर्ट करें मैं List[Option[T]] को Option[List[T]] में बदलना चाहता हूं। समारोह के हस्ताक्षर प्रकारस्कालज़

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] 

अपेक्षित व्यवहार एक सूची है कि तत्वों Some के अंदर तत्वों की सूची से युक्त एक Some में केवल Some रों में मैप करने के लिए है। दूसरी तरफ, यदि इनपुट सूची में कम से कम एक None है, तो अपेक्षित व्यवहार केवल None वापस करना है। उदाहरण के लिए:

scala> lo2ol(Some(1) :: Some(2) :: Nil) 
res10: Option[List[Int]] = Some(List(1, 2)) 

scala> lo2ol(Some(1) :: None :: Some(2) :: Nil) 
res11: Option[List[Int]] = None 

scala> lo2ol(Nil : List[Option[Int]]) 
res12: Option[List[Int]] = Some(List()) 

एक उदाहरण कार्यान्वयन, scalaz बिना, होगा:

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = { 
    lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => (o, ol) match { 
    case (Some(x), Some(xs)) => Some(x :: xs); 
    case _ => None : Option[List[T]]; 
}}} 

मैंने कहीं एक ऐसी ही उदाहरण देखकर याद है, लेकिन Scalaz का उपयोग कर कोड को आसान बनाने के। यह कैसा दिखता है?


एक से थोड़ा अधिक संक्षिप्त संस्करण, Scala2.8 PartialFunction.condOpt का उपयोग कर, लेकिन अभी भी Scalaz बिना:

import PartialFunction._ 

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = { 
    lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => condOpt(o, ol) { 
    case (Some(x), Some(xs)) => x :: xs 
    } 
}} 

उत्तर

20

एक ऐसा फ़ंक्शन है जो List[Option[A]] को स्कालज़ में Option[List[A]] में बदल देता है। यह sequence है। मामले से कोई भी तत्व None हैं None हो और एक Some[List[A]] मामले में करने के लिए सभी तत्वों Some हैं, तो आप सिर्फ यह कर सकते हैं:

import scalaz.syntax.traverse._ 
import scalaz.std.list._  
import scalaz.std.option._ 

lo.sequence 

इस विधि वास्तव में बदल जाता है G[F[A]] में F[G[A] दिया Traverse[F] के एक कार्यान्वयन मौजूद है , और Applicative[G] (Option और List दोनों को संतुष्ट करने और उन आयातों द्वारा प्रदान किए जाने के लिए होता है)।

Applicative[Option] के शब्दों में इस तरह है कि अगर एक Option की List रों के तत्वों के किसी भी कर रहे हैं None, तो sequenceNone रूप में अच्छी तरह हो जाएगा रहे हैं। आप किसी भी अन्य मूल्यों None हो या न सब Some मानों की सूची प्राप्त करना चाहते हैं, तो आप ऐसा कर सकते हैं:

lo flatMap (_.toList) 

आप किसी भी Monad भी एक Monoid (List होता है होने के लिए रूपों कि के लिए कि सामान्यीकरण कर सकते हैं इनमें से किसी एक):

import scalaz.syntax.monad._ 

def somes[F[_],A](x: F[Option[A]]) 
       (implicit m: Monad[F], z: Monoid[F[A]]) = 
    x flatMap (o => o.fold(_.pure[F])(z.zero)) 
+1

लेकिन यदि आप 'सूची [विकल्प [ए]]' के 'कुछ' मान चाहते हैं, तो आपको अब 'विकल्प' की आवश्यकता नहीं है। आपके पास या तो एक खाली सूची या 'ए' की एक nonempty सूची होगी। – Apocalisp

+0

वास्तव में, यदि कोई भी तत्व 'कोई नहीं' है, तो मैं 'कोई नहीं' प्राप्त करना चाहता हूं, इसलिए 'अनुक्रम' वही है जो मैंने पूछा था। मैं आवश्यकता को स्पष्ट करने के लिए प्रश्न को संपादित करने का प्रयास करूंगा। –

+0

मुझे काम करने के लिए 'lo.sequence' नहीं मिल सकता है। स्कैला-2.8.0. बीटा 1 और स्केलज़-कोर_2.8.0.टाटा 1-5.0.1-SNAPSHOT.jar का उपयोग करना, अगर मैं 'def lo2ol [टी] दर्ज करता हूं (लो: सूची [विकल्प [टी]]): विकल्प [सूची [ टी]] = lo.sequence', मुझे ': 10: त्रुटि: प्रकार scalaz के लिए निहित विस्तार अलग करना। आवेदनक [एन]' –

15

किसी कारण से आप नापसंद करते हैं

if (lo.exists(_ isEmpty)) None else Some(lo.map(_.get)) 

? स्कालाज़ के बिना स्कैला में शायद यह सबसे छोटा है।

+2

बुरा नहीं का विकल्प देता है, लेकिन यह दो बार सूची पर चला जाता है। – Apocalisp

+3

@Apocalisp: दरअसल, लेकिन यह सब कुछ छोड़ने और वापस 'कोई नहीं' फेंकने से पहले एक नई सूची बनाने से लगभग हमेशा सस्ता है। –

+1

मुझे 'Option.get' को नापसंद करना पड़ता है, लेकिन यह ठीक है। –

2

Scalaz में Applicative[Option] गलत व्यवहार सीधे MA#sequence उपयोग करने के लिए है, तब तक आप भी एक Applicative एक Monoid से निकाले जाते हैं कर सकते हैं। यह MA#foldMapDefault या MA#collapse के साथ सुविधाजनक बना दिया गया है।

इस मामले में, हम Monoid[Option[List[Int]] का उपयोग करते हैं।हम पहले एक तत्व के List एस में व्यक्ति Int एस को लपेटने के लिए एक आंतरिक मानचित्र (MA#∘∘) निष्पादित करते हैं। दुख की बात है

def co2oc[C[_], A](cs: C[Option[A]]) 
        (implicit ct: Traverse[C], cp: Pointed[C], cam: Monoid[C[A]]): Option[C[A]] = 
    (cs ∘∘ {(_: A).pure[C]}).collapse 


co2oc(List(some(1), none[Int], some(2))) assert_≟ some(List(1, 2)) 
co2oc(Stream(some(1), none[Int], some(2))) assert_≟ some(Stream(1, 2)) 
co2oc(List(none[Int]))      assert_≟ none[List[Int]] 
co2oc(List[Option[Int]]())     assert_≟ none[List[Int]] 

, वर्तमान में या तो #2741 से चलाता है या एक अनंत लूप में संकलक भेजता है इस कोड को संकलित करने के लिए कोशिश कर रहा है:

(List(some(1), none[Int], some(2)) ∘∘ {(i: Int) => List(i)}).collapse assert_≟ some(List(1, 2)) 
(List(none[Int]) ∘∘ {(i: Int) => List(i)}).collapse     assert_≟ none[List[Int]] 
(List[Option[Int]]() ∘∘ {(i: Int) => List(i)}).collapse    assert_≟ none[List[Int]] 

Traverse, Pointed और Monoid के लिए उदाहरणों के साथ किसी भी कंटेनर के लिए List से सार संक्षेप।

अद्यतन सूची में दो बार से गुजरने से बचने के लिए, मैं foldMapDefault का इस्तेमाल किया है चाहिए:

(List(some(1), none[Int], some(2)) foldMapDefault (_ ∘ ((_: Int).pure[List]))) 

इस उत्तर मूल अनुरोध है कि एक खाली सूची, या एक सूची से युक्त केवल None रों के आधार पर किया गया था, ऐसा करना चाहिए None वापस करें। संयोग से, यह Option[scalaz.NonEmptyList] - NonEmptyList द्वारा सबसे अच्छा मॉडल किया जाएगा, कम से कम एक तत्व की गारंटी देता है।

यदि आप सिर्फ List[Int] चाहते हैं, तो अन्य उत्तरों में दिए गए कई आसान तरीके हैं। दो प्रत्यक्ष तरीकों का उल्लेख नहीं किया गया है:

list collect { case Some(x) => x } 
list flatten 
+0

की आवश्यकता होगी इस प्रश्न के पीछे असली उपयोग-मामला अपोकलिस्प के उत्तर में' अनुक्रम 'विधि द्वारा पूरी तरह से पूरा किया गया है, जिसमें परिणामस्वरूप खाली सूची दी गई है (जो हो सकता है उपयोग मामले कहा)। लेकिन एनईएल प्रकार का उल्लेख करने के लिए धन्यवाद, यह आसान दिखता है। –

+0

@ पुन: शब्दकोष: राफेल ने लिखा "अगर इनपुट सूची में कम से कम एक नहीं है, तो अपेक्षित व्यवहार सिर्फ किसी को वापस नहीं करना है"। जहां आपने लिखा था "केवल एक सूची वाली सूची, किसी को भी वापस नहीं करना चाहिए"। यह बहुत अलग है। राफेल ने वास्तव में क्या पूछा, "सूची फ़्लैटन" नहीं करता है। –

+0

मेरा बुरा, मैंने सवाल का गलत व्याख्या किया। 'एमए # foldMapDefault' और 'एमए # अनुक्रम' दोनों 'ट्रैवर्स [सूची]' का उपयोग करते हैं, लेकिन एक अलग आवेदक फंक्चर का उपयोग करते हैं। – retronym

0

यह मेरे लिए काम करता है। मुझे आशा है कि यह एक सही समाधान है।

यह कोई नहीं देता है, तो सूची में से कोई एक विकल्प नहीं है, अन्यथा यह सूची [एक]

def sequence[A](a: List[Option[A]]): Option[List[A]] = { 

    a.foldLeft(Option(List[A]())) { 
    (prev, cur) => { 

     for { 
     p <- prev if prev != None 
     x <- cur 
     } yield x :: p 

    } 
    } 

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