2012-01-20 7 views
7

में विकल्प से भरे केस क्लास को कैसे खींचूं मैं स्कैला के लिए बहुत नया हूं और मैं अभी भी वाक्यविन्यास और शैली में उपयोग करने की कोशिश कर रहा हूं, इसलिए यह शायद एक बहुत ही सरल सवाल है।मैं स्कैला

मैं एक codebase जहां मामले कक्षाएं तो जैसे विकल्पों के साथ आबादी के बहुत देखते हैं के साथ काम कर रहा हूँ:

case class Person(
    pants: Option[Pants] 
) 
case class Pants(
    pocket: Option[Pocket] 
) 
case class Pocket(
    cash: Option[Cash] 
) 
case class Cash(
    value: String = "zilch" 
) 

उपरोक्त उदाहरण में, कैसे आप कितना पैसा लौटने के बारे में जाना होगा एक Person में है ' एस PantsPocket, अगर वे वास्तव में पैंट पहन रहे हैं ... जेब के साथ, और यदि उनके पास कोई पैसा है?

उत्तर

8

for-comprehensions के लिए एक महान समय:

val someCash: Option[Cash] = 
    for(pants <- somePerson.pants; 
     pocket <- pants.pocket; 
     cash <- pocket.cash) yield cash 

इसके तुल्य आप निम्नलिखित है, जिसके लिए पहले कोड वाक्यात्मक चीनी है (कुछ बारीकियों अनदेखी) लिख सकते हैं:

val someCash: Option[Cash] = 
    somePerson.pants.flatMap(_.pocket.flatMap(_.cash)) 

(मैं नहीं कर रहा हूँ पूरी तरह से सुनिश्चित करें कि अगर आप _ वाइल्डकार्ड का उपयोग करके अंतिम अभिव्यक्ति लिख सकते हैं, जैसा मैंने किया था)।

+0

बहुत बढ़िया, धन्यवाद! समझदारी दृष्टिकोण वास्तव में वही है जो मैं करने की कोशिश कर रहा था, लेकिन जिस संरचना के साथ मैं काम कर रहा हूं वह उतना स्वच्छ नहीं है जितना मैंने ऊपर दिया था। कम से कम यह पुष्टि करता है कि मैं सही रास्ते पर हूं। –

2

ziggystar का जवाब मैं क्या प्रयोग करेंगे है, लेकिन पूर्णता के लिए, पैटर्न मिलान भी इस्तेमाल किया जा सकता जैसे,

val someCash: Option[Cash] = person match { 
    case Person(Some(Pants(Some(Pocket(Some(cash)))))) => Some(cash) 
    case _ => None 
} 
6

सवाल डेटा को संशोधित जिक्र नहीं किया था, लेकिन आप क्या करने की जरूरत है जब यह आपको जल्दी से स्केल लाइब्रेरी में यह आसान बनाने के लिए टूल नहीं है (जब डेटा अपरिवर्तनीय है)। यदि आपने अभी तक इसका अनुभव नहीं किया है, तो प्रश्न में परिभाषित प्रकारों का उपयोग करके Person द्वारा आयोजित Cash के value को प्रतिस्थापित करने या संशोधित करने वाले फ़ंक्शन को लिखने का प्रयास करें।

टोनी मॉरिस 'Asymmetric Lenses in Scala, लेंस में वर्णित अनुसार इस समस्या का उचित समाधान है।

यहाँ हम कैसे जाएं और Scalaz की scalaz-seven शाखा से एक व्यक्ति के CashLens और PLens (आंशिक लेंस) कार्यान्वयन का उपयोग करने का value अद्यतन कर सकते हैं का एक उदाहरण है।

सबसे पहले, कुछ बॉयलरप्लेट: केस कक्षाओं के प्रत्येक क्षेत्र के लिए लेंस उदाहरण परिभाषित करें। A @[email protected] B का अर्थ Lens[A, B] जैसा है।

val pants: Person @[email protected] Option[Pants] = 
    lensG(_.pants, p => ps => p.copy(pants = ps)) 

val pocket: Pants @[email protected] Option[Pocket] = 
    lensG(_.pocket, ps => p => ps.copy(pocket = p)) 

val cash: Pocket @[email protected] Option[Cash] = 
    lensG(_.cash, p => c => p.copy(cash = c)) 

val value: Cash @[email protected] String = 
    lensG(_.value, c => v => c.copy(value = v)) 

हम इन लेंसों के सभी रचना नहीं कर सकते, तथापि, क्योंकि क्षेत्रों के सबसे Option प्रकार में लिपटे रहे हैं।

आंशिक लेंस बचाव के लिए: इन हमें पहुँच सकते हैं और एक संरचना है कि , इस तरह के मौजूद न हो एक Option की Some मूल्य, या एक List की head के रूप में की अद्यतन भागों करने देते हैं।

हम प्रत्येक वैकल्पिक फ़ील्ड को देखने वाले आंशिक लेंस बनाने के लिए स्कालज़ 7 से somePLens फ़ंक्शन का उपयोग कर सकते हैं।हमारे नियमित लेंस में से एक के साथ आंशिक लेंस लिखने के लिए, हमें विधि पर मौजूद partial विधि का उपयोग करके, नियमित लेंस के लिए समकक्ष आंशिक लेंस उदाहरण तक पहुंचने की आवश्यकता है।

// @-? is an infix type alias for PLens 
val someCash: Pocket @-? Cash = cash.partial andThen somePLens 

scala> someCash.get(Pocket(Some(Cash("zilch")))) 
res1: Option[Cash] = Some(Cash(zilch)) 

उसी तरह, हम सब अपने लेंस 'partial उदाहरणों लिखने, और somePLens के उदाहरण sandwiching द्वारा नकद एक Person द्वारा आयोजित देखने हमारे आंशिक लेंस बना सकते हैं। यहां, मैंने <=< ऑपरेटर का उपयोग किया है, andThen के लिए उपनाम (जो ऑपरेटरों के साथ compose के बराबर है)।

scala> someCashValue.get(ben) 
res2: Option[String] = Some(zilch) 

मूल्य को संशोधित करने के आंशिक लेंस का उपयोग करना: मैं नकदी की मूल्य तक पहुँचने के लिए आंशिक लेंस का प्रयोग

val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch"))))))) 

:

val someCashValue: Person @-? String = 
    pants.partial <=< somePLens <=< 
    pocket.partial <=< somePLens <=< 
    cash.partial <=< somePLens <=< 
    value.partial 

साथ खेलने के लिए एक Person उदाहरण बनाना :

scala> someCashValue.mod(_ + ", zero, nada", ben) 
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada))))))) 
(!) 0

अब, अगर मैं नहीं किसी भी पैंट पहने हुए है, हम देख सकते हैं कि मेरी नकदी की मूल्य को संशोधित करने की कोशिश कोई प्रभाव नहीं पड़ेगा:

scala> val ben = Person(None) 
ben: Person = Person(None) 

scala> someCashValue.mod(_ + ", zero, nada", ben) 
res4: Person = Person(None) 
12

Scalaz 7 एक छोटे से इतना बदल गया है यहां एक और उदाहरण है:

object PartialLensExample extends App { 

    import scalaz._ 
    import Lens._ 
    import PLens._ 


    case class Bar(blub: Option[String]) 
    case class Foo(bar: Option[Bar]) 

    // normal lenses for getting and setting values 
    val fooBarL: Foo @> Option[Bar] = lensg(foo ⇒ bar ⇒ foo.copy(bar = bar), _.bar) 
    val barBlubL: Bar @> Option[String] = lensg(bar ⇒ blub ⇒ bar.copy(blub = blub), _.blub) 

    // compose the above as 'Partial Lenses', >=> is just an alias for 'andThen' 
    val fooBarBlubL: Foo @?> String = fooBarL.partial >=> somePLens >=> barBlubL.partial >=> somePLens 

    // try it 
    val foo = Foo(Some(Bar(Some("Hi")))) 

    println(fooBarBlubL.get(foo)) // Some(Hi) 

    println(fooBarBlubL.set(foo, "Bye")) //Foo(Some(Bar(Some(Bye)))) 

    // setting values 
    val foo2 = Foo(None) 
    println(fooBarL.set(foo2, Some(Bar(None)))) // Foo(Some(Bar(None))) 

} 
+1

ग्रेट उत्तर, लेकिन उन नेस्टेड मानों को सेट करते समय यहां एक बड़ा ठोकरें ब्लॉक है: क्या होगा यदि कोई मान 'कोई नहीं' है? आपके उदाहरण में 'प्लेंस' केवल 'blub: विकल्प [स्ट्रिंग]' को असाइन करने की अनुमति देगा यदि 'blub' _ पहले से ही 'कुछ' value_ है। मैंने पाया है कि monadic राज्य संक्रमण शीर्ष स्तर के सदस्यों को शुरू कर सकते हैं, लेकिन मुझे अस्पष्ट नहीं है कि उन लोगों के लिए यह कैसे किया जाएगा। –

+1

हम्म, हाँ। मैंने फू पर बार सेट करने के लिए उत्तर अपडेट किया, लेकिन मुझे बार में ब्लब वैल्यू सेट करने का मुद्दा दिखाई देता है - मुझे उस पर विचार करने की आवश्यकता होगी। –

+0

['PLens' उदाहरणों का उपयोग करके राज्य संक्रमणों की एक उग्रता] (https://gist.github.com/michaelahlers/20ec194410f89422847fdd3a71777c69) सबसे अच्छा मैं कर सकता हूं, आवश्यकतानुसार गुणों को प्रारंभ करना (जो समझ में आता है, विशेष रूप से [ उत्तर पैंट शामिल] [http://stackoverflow.com/a/9978488/700420) @ बेन-जेम्स ने दिया)। यह मुझे बहुत श्रमिक के रूप में मारता है और यह स्केल नहीं करता है, इसलिए मुझे लगता है कि एक बेहतर दृष्टिकोण होना चाहिए। –