2015-03-02 10 views
10

मैं Functional Programming in Swift पुस्तक के माध्यम से काम कर रहा हूं, और मेरे पास विकल्प अध्याय में पेश की गई अवधारणा में अंतर को समझने का वास्तव में अच्छा तरीका नहीं है।स्विफ्ट फ़ंक्शनल प्रोग्रामिंग - "वैकल्पिक बाइंड" बनाम "वैकल्पिक मानचित्र"

पैटर्न जब optionals के साथ काम हो जाता है:

if let thing = optionalThing { 
    return doThing(thing) 
} 
else { 
    return nil 
} 

यह मुहावरा मानक पुस्तकालय समारोह map

map(optionalThing) { thing in doThing(thing) } 

किताब तो पर जारी है और वैकल्पिक की अवधारणा का परिचय के साथ संक्षेप नियंत्रित किया जाता है बाध्यकारी, जहां वह अंतर करने की मेरी क्षमता टूटने लगती है।

पुस्तक हमें गाइड map समारोह को परिभाषित करने के:

func map<T, U>(optional: T?, f: T -> U) -> U? 
{ 
    if let x = optional { 
     return f(x) 
    } 
    else { 
     return nil 
    } 
} 

और यह भी हमें गाइड एक वैकल्पिक बाध्यकारी समारोह को परिभाषित करने के। नोट: पुस्तक ऑपरेटर >>= का उपयोग करती है, लेकिन मैंने नामित फ़ंक्शन का उपयोग करना चुना है क्योंकि इससे मुझे समानताएं देखने में मदद मिलती है।

func optionalBind<T, U>(optional: T?, f: T -> U?) -> U? 
{ 
    if let x = optional { 
     return f(x) 
    } 
    else { 
     return nil 
    } 
} 

इन दोनों विधियों के लिए कार्यान्वयन मेरे जैसा दिखता है। दोनों के बीच फर्क सिर्फ इतना समारोह तर्क वे ले रहा है:

  • map एक समारोह है कि एक यू
  • optionalBind में एक टी बदल लेता है एक समारोह है कि एक वैकल्पिक यू
में एक टी बदल लेता है

func addOptionalsBind(optionalX: Int?, optionalY: Int?) -> Int? 
{ 
    return optionalBind(optionalX) { x in 
     optionalBind(optionalY) { y in 
      x + y 
     } 
    } 
} 

func addOptionalsMap(optionalX: Int?, optionalY: Int?) -> Int? 
{ 
    return map(optionalX) { x in 
     map(optionalY) { y in 
      x + y 
     } 
    } 
} 

    :

    "घोंसले" इन फ़ंक्शन कॉल का परिणाम मेरे मस्तिष्क दर्द होता है

  • addOptionalsBind फ़ंक्शन ठीक वही करता है जो आप करना चाहते हैं।
  • addOptionalsMap समारोह बताते हुए संकलित करने के लिए विफल रहता है:

    'इंट ??' परिवर्तनीय नहीं है 'Int?'

मुझे लगता है कि मैं यह समझने के करीब हूं कि यहां क्या हो रहा है (और वैकल्पिक पूर्णांक को वैकल्पिक रूप से फिर से लपेटा जा रहा है? लेकिन कैसे? क्यों? हू?), लेकिन मैं यह समझने से बहुत दूर हूं कि मैं पूछने के लिए एक स्मार्ट सवाल पूरी तरह से सुनिश्चित नहीं है।

उत्तर

5

क्या हो रहा है addOptionalsMap के अधिक वर्बोज़ कार्यान्वयन के साथ स्पष्ट हो सकता है। के map -instead क्या तुम वहाँ है करने के लिए अंतरतम कॉल के साथ शुरू करते हैं, चलो इस के बजाय का उपयोग करते हैं:

let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in 
    return x + y 
} 

map के लिए प्रदान की बंद एक Int और रिटर्न और Int, जबकि खुद map करने के लिए कॉल एक वैकल्पिक रिटर्न लेता है : Int?। कोई आश्चर्य नहीं है!के एक कदम बाहर ले जाने और देखो क्या होता है:

let mappedExternal: ??? = map(optionalX) { (x: ???) -> ??? in 
    let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in 
     return x + y 
    } 
    return mappedInternal 
} 

यहाँ हम ऊपर से हमारे mappedInternal मूल्य देख सकते हैं, लेकिन कुछ ही प्रकार अपरिभाषित छोड़ रहे हैं। map में (T?, T -> U) -> U? का हस्ताक्षर है, इसलिए हमें केवल यह पता लगाने की आवश्यकता है कि T और U इस मामले में हैं। हम बंद होने के वापसी मूल्य को जानते हैं, mappedInternal, Int? है, इसलिए UInt? बन गया है। दूसरी तरफ T, एक गैर-वैकल्पिक Int रह सकता है। इस स्थानापन्न, हम पाते हैं:

let mappedExternal: Int?? = map(optionalX) { (x: Int) -> Int? in 
    let mappedInternal: Int? = map(optionalY) { (y: Int) -> Int in 
     return x + y 
    } 
    return mappedInternal 
} 

बंद T -> U जो Int -> Int? का आकलन करती है, और पूरे map अभिव्यक्ति Int?? के लिए मानचित्रण Int? समाप्त होता है। आपके मन में क्या नहीं था!


कंट्रास्ट कि optionalBind का उपयोग कर संस्करण के साथ, पूरी तरह से टाइप- निर्दिष्ट:

let boundExternal: ??? = optionalBind(optionalX) { (x: ???) -> ??? in 
    let boundInternal: Int? = optionalBind(optionalY) { (y: Int) -> Int? in 
     return x + y 
    } 
    return boundInternal 
} 

के इस संस्करण के लिए उन ??? प्रकार पर नजर डालते हैं। optionalBind के लिए हमें T -> U? बंद करने की आवश्यकता है, और boundInternal में Int? वापसी मान है। तो दोनों T और U इस मामले में बस Int हो सकता है, और हमारे कार्यान्वयन इस तरह दिखता है:

let boundExternal: Int? = optionalBind(optionalX) { (x: Int) -> Int? in 
    let boundInternal: Int? = optionalBind(optionalY) { (y: Int) -> Int? in 
     return x + y 
    } 
    return boundInternal 
} 

आपका भ्रम रास्ता चर से आ सकती है optionals के रूप में "उठाया" जा सकता है। यह जब एक परत के साथ काम करने को देखने के लिए आसान है:

func optionalOpposite(num: Int?) -> Int? { 
    if let num = num { 
     return -num 
    } 
    return nil 
} 

optionalOpposite, किसी भी प्रकार Int? के एक चर के साथ कहा जा सकता है जैसे कि यह स्पष्ट रूप से उम्मीद है, या प्रकार Int की एक गैर वैकल्पिक चर। इस दूसरे मामले में, गैर-वैकल्पिक चर को कॉल के दौरान पूर्ण रूप से वैकल्पिक (यानी उठाया गया) में परिवर्तित किया जाता है।

map(x: T, f: T -> U) -> U? अपने रिटर्न मूल्य में उठाना कर रहा है। चूंकि f को T -> U के रूप में घोषित किया गया है, यह कभी भी वैकल्पिक U? देता है। फिर भी के U? के वापसी मूल्य का अर्थ है कि f(x) पर वापस U? पर ले जाया गया है।

आपके उदाहरण में, आंतरिक बंद x + y, Int को Int? पर ले जाया गया है। उस मान को फिर Int?? पर फिर से उठाया जाता है जिसके परिणामस्वरूप मिलान मेल नहीं होता है, क्योंकि आपने को Int? वापस करने के लिए घोषित किया है।

12
  • नक्शा एक समारोह है कि एक यू
  • optionalBind में एक टी बदल लेता है एक समारोह है कि

वास्तव में एक वैकल्पिक यू में एक टी बदल लेता है। वह पूरा अंतर है।आइए वास्तव में एक सरल कार्य, lift() पर विचार करें। यह T को T? में परिवर्तित करने जा रहा है। (हास्केल में, उस फ़ंक्शन को return कहा जाएगा, लेकिन यह गैर-हास्केल प्रोग्रामर के लिए थोड़ा उलझन में है, और इसके अलावा, return एक कीवर्ड है)।

func lift<T>(x: T) -> T? { 
    return x 
} 

println([1].map(lift)) // [Optional(1)] 

ग्रेट। अब क्या होगा यदि हम इसे फिर से करते हैं:

println([1].map(lift).map(lift)) // [Optional(Optional(1))] 

हमम। तो अब हमारे पास Int?? है, और इससे निपटने के लिए दर्द होता है। हम वास्तव में सिर्फ एक स्तर का वैकल्पिकता चाहते हैं। आइए ऐसा करने के लिए एक फ़ंक्शन बनाएं। हम इसे flatten पर कॉल करेंगे और एकल-वैकल्पिक के लिए डबल-वैकल्पिक को फ़्लैट-डाउन करेंगे।

func flatten<T>(x: T??) -> T? { 
    switch x { 
    case .Some(let x): return x 
    case .None : return nil 
    } 
} 

println([1].map(lift).map(lift).map(flatten)) // [Optional(1)] 

बहुत बढ़िया। बस हम क्या चाहते थे। आप जानते हैं कि .map(flatten) बहुत कुछ होता है, तो चलो इसे एक नाम दें: flatMap (जो स्कैला कॉल जैसी भाषाएं हैं)। खेल के कुछ मिनट आपको साबित करना चाहिए कि flatMap() का कार्यान्वयन बिल्कुल bindOptional का कार्यान्वयन है और वे वही काम करते हैं। एक वैकल्पिक और कुछ ऐसा जो वैकल्पिक विकल्प देता है, और इसके बाहर "वैकल्पिक-नेस" का केवल एक स्तर प्राप्त करें।

यह वास्तव में सामान्य समस्या है। यह इतना आम है कि हास्केल के लिए एक अंतर्निहित ऑपरेटर है (>>=)। यह इतना आम है कि स्विफ्ट भी में के लिए एक अंतर्निहित ऑपरेटर है यदि आप कार्यों के बजाय विधियों का उपयोग करते हैं।

struct Name { 
    let first: String? = nil 
    let last: String? = nil 
} 

struct Person { 
    let name: Name? = nil 
} 

let p:Person? = Person(name: Name(first: "Bob", last: "Jones")) 
println(p?.name?.first) // Optional("Bob"), not Optional(Optional(Optional("Bob"))) 

?. वास्तव में सिर्फ flatMap (*) है जो: यह वैकल्पिक चेन (यह एक वास्तविक शर्म की बात है कि स्विफ्ट कार्यों को यह विस्तार नहीं करता है, लेकिन स्विफ्ट एक बहुत अधिक तरीकों की तुलना में यह काम करता है प्यार करता है प्यार करता है) कहा जाता है वास्तव में बस bindOptional। अलग-अलग नाम क्यों? खैर, यह पता चला है कि "नक्शा और फिर flatten" एक और विचार के बराबर है जो मोनैडिक बाइंड कहा जाता है जो इस समस्या के बारे में सोचता है। हाँ, monads और वह सब। यदि आप मोनैड (जो यह है) के रूप में T? के बारे में सोचते हैं, तो flatMap आवश्यक बाइंड ऑपरेशन के रूप में सामने आता है। (तो "बाइंड" एक सामान्य शब्द है जो सभी मोनैड पर लागू होता है, जबकि "फ्लैट मैप" कार्यान्वयन के विवरण को संदर्भित करता है। मुझे "फ्लैट मैप" लोगों को पहले सिखाने में आसान लगता है, लेकिन वाईएमएमवी।)

यदि आप चाहते हैं इस चर्चा का एक लंबा संस्करण और यह Optional से अन्य प्रकारों पर कैसे लागू हो सकता है, Flattenin' Your Mappenin' देखें।

(*) .? आपके द्वारा पारित होने के आधार पर map भी हो सकता है। यदि आप T->U? पास करते हैं तो यह flatMap है। यदि आप T->U पास करते हैं तो आप या तो map के रूप में सोच सकते हैं, या आप अभी भी flatMap होने के बारे में सोच सकते हैं जहां U को U? (जो स्विफ्ट स्वचालित रूप से स्वचालित रूप से) पर प्रचारित किया जाता है।

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