मैं एक बुनियादी प्रोटोकॉल एक्सटेंशन को लागू करने की कोशिश कर रहा हूं जैसे:

protocol Value {
    func get() -> Float
    mutating func set(to:Float)
}
extension Value {
    static func min(of a:Value, and b:Value) -> Float {
        if a < b { //Expression type 'Bool' is ambiguous without more context
            return a.get()
        }else{
            return b.get()
        }
    }
    static func < (a:Value, b:Value) -> Bool {
        return a.get() < b.get()
    }
}

if क्लॉज में कंपाइलर कहता है:Expression type 'Bool' is ambiguous without more context। यह काम क्यों नहीं करता?

1
gloo 21 पद 2017, 22:30

3 जवाब

सबसे बढ़िया उत्तर

जैसा कि इस प्रश्नोत्तर में पर स्पर्श किया गया है, static सदस्यों के रूप में लागू किए गए ऑपरेटर ओवरलोड और ऑपरेटर ओवरलोड के रूप में लागू किए गए के बीच अंतर है शीर्ष स्तर के कार्य। static सदस्य एक अतिरिक्त (अंतर्निहित) self पैरामीटर लेते हैं, जिसे संकलक को अनुमान लगाने में सक्षम होना चाहिए।

तो self का मान कैसे निकाला जाता है? खैर, इसे या तो ऑपरेंड या ओवरलोड के रिटर्न प्रकार से किया जाना है। प्रोटोकॉल एक्सटेंशन के लिए, इसका मतलब है कि उनमें से एक प्रकार Self होना चाहिए। ध्यान रखें कि आप किसी प्रकार के ऑपरेटर को सीधे कॉल नहीं कर सकते (अर्थात आप (Self.<)(a, b) नहीं कह सकते)।

निम्नलिखित उदाहरण पर विचार करें:

protocol Value {
  func get() -> Float
}

extension Value {
  static func < (a: Value, b: Value) -> Bool {
    print("Being called on conforming type: \(self)")
    return a.get() < b.get()
  }
}

struct S : Value {
  func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // Ambiguous reference to member '<'

< को कॉल करने में self का मान क्या है? संकलक इसका अनुमान नहीं लगा सकता (वास्तव में मुझे लगता है कि इसे सीधे अधिभार पर त्रुटि करनी चाहिए क्योंकि यह अन-कॉल करने योग्य है)। ध्यान रखें कि self एक प्रोटोकॉल एक्सटेंशन में स्थिर दायरे में एक ठोस अनुरूप प्रकार होना चाहिए; यह केवल Value.self नहीं हो सकता (क्योंकि प्रोटोकॉल एक्सटेंशन में स्थिर विधियां केवल ठोस अनुरूप प्रकारों पर कॉल करने के लिए उपलब्ध हैं, प्रोटोकॉल प्रकार पर नहीं)।

हम इसके बजाय एक शीर्ष-स्तरीय फ़ंक्शन के रूप में अधिभार को परिभाषित करके उपरोक्त उदाहरण और आपके उदाहरण दोनों को ठीक कर सकते हैं:

protocol Value {
  func get() -> Float
}

func < (a: Value, b: Value) -> Bool {
  return a.get() < b.get()
}

struct S : Value {
  func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // false

यह काम करता है क्योंकि अब हमें self के लिए कोई मान निकालने की आवश्यकता नहीं है।

हम कंपाइलर को self के मान का अनुमान लगाने का एक तरीका भी दे सकते थे, जिससे एक या दोनों पैरामीटर Self ले सकते थे:

protocol Value {
  func get() -> Float
}

extension Value {
  static func < (a: Self, b: Self) -> Bool {
    print("Being called on conforming type: \(self)")
    return a.get() < b.get()
  }
}

struct S : Value {
  func get() -> Float { return 0 }
}

let s = S()
print(s < s)

//  Being called on conforming type: S
//  false

कंपाइलर अब स्टैटिक प्रकार के ऑपरेंड से self का अनुमान लगा सकता है। हालांकि, जैसा कि ऊपर कहा गया है, यह एक ठोस प्रकार होना चाहिए, ताकि आप विषमयुग्मित Value ऑपरेंड से निपट न सकें (आप Value लेकर एक ऑपरेंड के साथ काम कर सकते हैं; लेकिन दोनों नहीं तब self का अनुमान लगाने का कोई तरीका नहीं होगा)।


हालांकि ध्यान दें कि यदि आप < का डिफ़ॉल्ट कार्यान्वयन प्रदान कर रहे हैं, तो आपको संभवतः == का डिफ़ॉल्ट कार्यान्वयन भी प्रदान करना चाहिए। जब तक आपके पास ऐसा न करने का कोई अच्छा कारण न हो, मैं आपको यह भी सलाह दूंगा कि इन अधिभारों को समरूप कंक्रीट ऑपरेंड (यानी Self प्रकार के पैरामीटर) लें, ताकि वे Comparable के लिए एक डिफ़ॉल्ट कार्यान्वयन प्रदान कर सकें।

इसके अलावा get() और set(to:) आवश्यकताओं के बजाय, मैं इसके बजाय एक व्यवस्थित संपत्ति की आवश्यकता की सलाह दूंगा:

// Not deriving from Comparable could be useful if you need to use the protocol as
// an actual type; however note that you won't be able to access Comparable stuff,
// such as the auto >, <=, >= overloads from a protocol extension.
protocol Value {
  var floatValue: Double { get set }
}

extension Value {

  static func == (lhs: Self, rhs: Self) -> Bool {
    return lhs.floatValue == rhs.floatValue
  }

  static func < (lhs: Self, rhs: Self) -> Bool {
    return lhs.floatValue < rhs.floatValue
  }
}

अंत में, यदि Comparable अनुरूपता के लिए Value अनुरूपता आवश्यक है, तो आपको इसे Comparable से प्राप्त करना चाहिए:

protocol Value : Comparable {
  var floatValue: Double { get set }
}

आपको किसी भी मामले में min(of:and:) फ़ंक्शन की आवश्यकता नहीं होनी चाहिए, क्योंकि जब अनुरूप प्रकार Comparable के अनुरूप होता है, तो यह शीर्ष-स्तरीय min(_:_:) फ़ंक्शन का उपयोग कर सकता है।

1
Hamish 22 पद 2017, 00:47

आप नहीं लिख सकते

if a < b {

क्योंकि a और b का प्रकार Value है जो Comparable नहीं है।

हालांकि आप a और b से जुड़े float मान की तुलना कर सकते हैं

if a.get() < b.get() {
1
Luca Angeletti 21 पद 2017, 22:33

यदि आप ऐसे प्रकार बनाने में सक्षम होना चाहते हैं जो ऑपरेटरों का उपयोग कर सकते हैं जैसे >, <, ==, आदि, तो उन्हें Comparable प्रोटोकॉल का पालन करना होगा:

 protocol Value: Comparable {
    func get() -> Float
    mutating func set(to: Float)
}

हालांकि यह अधिक प्रतिबंधों के साथ आता है। आपको प्रोटोकॉल एक्सटेंशन के सभी Value प्रकारों को Self में बदलना होगा:

extension Value {
    static func min(of a: Self, and b: Self) -> Float {
        if a < b { //Expression type 'Bool' is ambiguous without more context
            return a.get()
        }else{
            return b.get()
        }
    }

    static func < (a: Self, b: Self) -> Bool {
        return a.get() < b.get()
    }
}

Self प्रकारों को उस प्रकार से बदल दिया जाता है जो प्रोटोकॉल लागू करता है। इसलिए यदि मैंने Value को एक प्रकार Container पर लागू किया है, तो विधियों के हस्ताक्षर इस तरह दिखाई देंगे:

class Container: Value {
    static func min(of a: Container, and b: Container) -> Float

    static func < (a: Container, b: Container) -> Bool
}

एक साइड नोट के रूप में, यदि आप Value को Comparable के अनुरूप बनाना चाहते हैं, तो आप == ऑपरेटर को Value एक्सटेंशन में भी जोड़ सकते हैं:

static func <(lhs: Self, rhs: Self) -> Bool {
    return lhs.get() < rhs.get()
}
0
Caleb Kleveter 22 पद 2017, 00:14