2015-12-24 7 views
5

मैंने स्विफ्ट जेनिक्स का उपयोग करके असतत रूपांतरण के लिए एल्गोरिदम के दो संस्करण टाइप किए। पूर्णांक संस्करण काम करता है। "दो 'टी' ऑपरेंड के लिए लागू नहीं किया जा सकता द्विआधारी ऑपरेटर '*' 'स्विफ्ट जेनिक्स: इंटीजर टाइप संस्करण वर्क्स लेकिन फ़्लोटिंग पॉइंट टाइप

import Foundation 
import Swift 

func linconv<T: IntegerType>(signal_A signal_A: [T], signal_B: [T]) -> [T]? { 

    // guard 
    guard signal_A.isEmpty == false && signal_B.isEmpty == false else { 
     return nil 
    } 

    // reverse at least one of the arrays 
    //let signal_A_reversed = Array(signal_A.reverse()) 

    // size of new array 
    let N = signal_A.count + signal_B.count - 1 

    // new array for result 
    var resultSignal = [T](count: N, repeatedValue: 0) 

    for n in 0..<N { 

     for j in 0...n { 

      if j < signal_B.count && (n - j) < signal_A.count { 

       resultSignal[n] += signal_B[j] * signal_A[n - j] 
      } 
     } 
    } 

    return resultSignal 
} 

func linconv<T: FloatingPointType>(signal_A signal_A: [T], signal_B: [T]) -> [T]? { 

    // guard 
    guard signal_A.isEmpty == false && signal_B.isEmpty == false else { 
     return nil 
    } 

    // reverse at least one of the arrays 
    //let signal_A_reversed = Array(signal_A.reverse()) 

    // size of new array 
    let N = signal_A.count + signal_B.count - 1 

    // new array for result 
    var resultSignal = [T](count: N, repeatedValue: T(0)) 

    for n in 0..<N { 

     for j in 0...n { 

      if j < signal_B.count && (n - j) < signal_A.count { 

       resultSignal[n] += signal_B[j] * signal_A[n - j] // compiler says error here! 
      } 
     } 
    } 

    return resultSignal 
} 

FloatingPointType संस्करण के लिए, संकलक का कहना है: लेकिन चल बिन्दु संस्करण गुणा के साथ कोई समस्या नहीं है। लेकिन, यह इंटीजर टाइप संस्करण पर ऐसा नहीं करता है। क्यूं कर?

उत्तर

5

FloatingPointType प्रोटोकॉल वास्तव में Double और Float प्रकार द्वारा अपनाई गई है, लेकिन इसके विपरीत, प्रोटोकॉल, किसी कारण से, शामिल नहीं है ब्लूप्रिंट जैसे ऑपरेटर विधियों (आपके मामले में), * द्विआधारी ऑपरेटर या += के लिए है असाइनमेंट ऑपरेटर। यहां महत्व दें कि सिर्फ कुछ ज्ञात प्रकार प्रोटोकॉल को अपनाने के लिए, उस प्रोटोकॉल में आवश्यक रूप से उन सभी ब्लूप्रिंट शामिल नहीं होना चाहिए जिन्हें हम उन प्रकारों के लिए उम्मीद करेंगे जिन्हें हमने अपनाया है।

दूसरी ओर IntegerType प्रोटोकॉल, ऑपरेटर विधियों के लिए ब्लूप्रिंट शामिल है।

इसलिए, आपके जेनेरिक T प्रोटोकॉल FloatingPointType के अनुरूप होने की आवश्यकता नहीं है (तेजी से आंखों में) बहुगुणनीय और इसी तरह, प्रोटोकॉल में ऐसे परिचालनों के लिए कोई ब्लूप्रिंट शामिल नहीं है। अगर हम standard library reference for FloatingPointType देखते हैं, तो हम देखते हैं कि यह Double, Float (और CGFloat) द्वारा अपनाया गया है। हम इन तीनों प्रकारों को अच्छी तरह से जानते हैं, अपने नियमित ऑपरेटरों के साथ, इसलिए, हम उन ऑपरेटरों का उपयोग FloatingPointType के अनुरूप सामान्य रूप से क्यों नहीं कर सकते? दोबारा, प्रोटोकॉल के अनुरूप होने वाले प्रकारों का संग्रह वास्तव में में प्रोटोकॉल वाले ब्लूप्रिंट में कोई अंतर्दृष्टि नहीं देता है।

उदाहरण के लिए, निम्नलिखित प्रोटोकॉल पर देखने के कुछ बुनियादी प्रकार के विस्तार यह

protocol ReallyLotsOfAdditionalStuff {} 
extension Int : ReallyLotsOfAdditionalStuff {} 
extension Double : ReallyLotsOfAdditionalStuff {} 

को यह डमी प्रोटोकॉल के लिए पुस्तकालय संदर्भ अनुरूप है कि केवल प्रकार सूची जाएगा Int और Doubleयह अपनाने। इसके विपरीत, अगर हम सावधान नहीं हैं, तो हम उम्मीद कर सकते हैं कि प्रोटोकॉल ReallyLotsOfAdditionalStuff के अनुरूप जेनरिक कम से कम कहेंगे, अतिरिक्त (अतिरिक्त सामानों के अतिरिक्त), लेकिन स्वाभाविक रूप से, यह मामला नहीं है।


वैसे भी, आप स्वयं वह ठीक कर सकते हैं, हालांकि, एक नए प्रोटोकॉल आप अपने FloatingPointType समारोह में जेनेरिक T के लिए एक अतिरिक्त प्रकार बाधा के रूप में जोड़ देते हैं बनाने के द्वारा।

protocol MyNecessaryFloatingPointTypeOperations { 
    func *(lhs: Self, rhs: Self) -> Self 
    func += (inout lhs: Self, rhs: Self) 

    // ... other necessary floating point operator blueprints ... 
} 

extension Float: MyNecessaryFloatingPointTypeOperations {} 
extension Double: MyNecessaryFloatingPointTypeOperations {} 

// Example: only type constraint to FloatingPointType 
func errorFloatingPointType<T: FloatingPointType> (a: T, b: T) -> T { 
    return a * b // Error: binary operator '*' cannot be applied to two 'T' operands 
} 

// Example: additional type constraint to your custom protocol 
func noErrorFloatingPointType<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>> (a: T, b: T) -> T { 
    return a * b // ok! 
} 
इसलिए

, अपने FloatingPointType ठीक, समारोह शीर्षक में T के लिए एक अतिरिक्त प्रकार बाधा के रूप में अपने कस्टम प्रोटोकॉल जोड़ने के लिए:

func linconv<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>>(signal_A: [T], signal_B: [T]) -> [T]? { 
    // ... 
} 

वैकल्पिक रूप से, अपने स्वयं के प्रोटोकॉल के वारिस FloatingPointType जाने और किसी भी अतिरिक्त पद्धतियां जोड़ने आपके "बाल प्रोटोकॉल" की आवश्यकता है, उदाहरण के लिए:

protocol ImprovedFloatingPointType : FloatingPointType { 
    func *(lhs: Self, rhs: Self) -> Self 
    func += (inout lhs: Self, rhs: Self) 
    // ... other necessary integer and floating point blueprints 
} 

extension Float: ImprovedFloatingPointType {} 
extension Double: ImprovedFloatingPointType {} 

func linconv<T: ImprovedFloatingPointType>(signal_A: [T], signal_B: [T]) -> [T]? { 
    // ... 
} 

अंत में, हम, पूछना हम भी पहली जगह में FloatingPointType प्रोटोकॉल की आवश्यकता है हो सकता है (यहां तक ​​कि हमारे कस्टम एक करने के लिए माता पिता के प्रोटोकॉल के रूप में)? हम केवल दो तेज चल बिंदु प्रकार Double और Float से निपटने के लिए एक सामान्य बनाना चाहते हैं, तो हम साथ ही केवल प्रोटोकॉल MyNecessaryFloatingPointTypeOperations एक प्रकार बाधा के रूप में हमारे सामान्य करने के लिए लागू हो सकता है:

func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T { 
    // ... 
    return a * b 
} 

आप पहले से ही जानते होंगे, हमें एक ब्लूप्रिंट के लिए FloatingPointType प्रोटोकॉल के अनुरूप जेनेरिक की आवश्यकता है: हमारे जेनेरिक फ़ंक्शन का पता लगाने के लिए कि हम एक पूर्णांक प्रारंभकर्ता, अर्थात् init(_ value: Int) का उपयोग करके T उदाहरण प्रारंभ कर सकते हैं। उदाहरण के लिए, अपने कार्य में:

// new array for result 
var resultSignal = [T](count: N, repeatedValue: T(0)) // <-- 

हालांकि, अगर यह केवल खाका हम FloatingPointType प्रोटोकॉल से उपयोग कर रहे हैं, हम भी इसे एक खाका के रूप में अपने स्वयं के प्रोटोकॉल के बजाय जोड़ सकते हैं और सामान्य प्रकार हटाने FloatingPointType पूरी तरह से बाधा।

protocol MyNecessaryFloatingPointTypeOperations { 
    func *(lhs: Self, rhs: Self) -> Self 
    func += (inout lhs: Self, rhs: Self) 

    init(_ value: Int) 

    // ... other necessary floating point blueprints 
} 

extension Float: MyNecessaryFloatingPointTypeOperations {} 
extension Double: MyNecessaryFloatingPointTypeOperations {} 

func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T { 
    // ... 
    var c = T(0) // OK 
    c += a * b // OK 
    return c 
} 

इस के साथ, हम महसूस करते हैं कि हम वास्तव में पूर्णांक प्रकार के लिए दो अलग-अलग जेनरिक और चल बिन्दु प्रकार की जरूरत नहीं है। चूंकि हम (आपके उदाहरण के लिए) केवल की आवश्यकता है 1. एक बाय-इंट-स्टार्टिज़र, 2. * बाइनरी ऑपरेटर, और 3. + = असाइनमेंट ऑपरेटर, हम इन ब्लू अंकों के अनुरूप सभी प्रकार के लिए जेनेरिक बना सकते हैं। बाधा टाइप करें, और इस प्रकार के प्रोटोकॉल द्वारा हमारे जेनेरिक द्वारा कवर किए जाने वाले प्रकारों का विस्तार करें।

protocol MyCustomProtocol { 
    func *(lhs: Self, rhs: Self) -> Self 
    func += (inout lhs: Self, rhs: Self) 

    init(_ value: Int) 

    // ... other necessary integer and floating point blueprints 
} 

extension Int: MyCustomProtocol {} 
extension Float: MyCustomProtocol {} 
extension Double: MyCustomProtocol {} 

func myIntAndFloatGenericFunction<T: MyCustomProtocol> (a: T, _ b: T) -> T { 
    // ... 
    var c = T(0) // OK 
    c += a * b // OK 
    return c 
} 

let aInt = 2 
let bInt = 3 
let aInt32: Int32 = 2 
let bInt32: Int32 = 3 
let aDouble = 2.5 
let bDouble = 3.0 

let cInt = myIntAndFloatGenericFunction(aInt, bInt) // 6 
let cInt32 = myIntAndFloatGenericFunction(aInt32, bInt32) // error 
let cDouble = myIntAndFloatGenericFunction(aDouble, bDouble) // 7.5 

यहाँ, तथापि, हम मौजूदा IntegerType प्रोटोकॉल का उपयोग कर में से एक लाभ देखें: यह पहले से ही, कई पूर्णांक प्रकार द्वारा अपनाया जाता है, जबकि हमारे कस्टम प्रोटोकॉल के लिए, इन सब int प्रकार (हम में उन्हें उपयोग करना चाहते हैं हमारे सामान्य) को हमारे कस्टम प्रोटोकॉल को अपनाने के लिए स्पष्ट रूप से विस्तारित करने की आवश्यकता है।

इसे लपेटने के लिए: यदि आप स्पष्ट रूप से जानते हैं कि आप अपने जेनेरिक द्वारा किस प्रकार के कवर करना चाहते हैं, तो आप अपने स्वयं के कस्टम प्रोटोकॉल को लिख सकते हैं और इन प्रकारों को अनुकूलित करने के लिए बढ़ा सकते हैं। यदि आप बाद में के लिए प्रोटोकॉल IntegerType प्रोटोकॉल के साथ, इन्ट्स और फ्लोट्स के लिए दो अलग जेनेरिक का उपयोग करके सभी (असंख्य) पूर्ण पूर्णांक प्रकार चाहते हैं, तो शायद यह पसंद करना है।

+0

यह कानूनी लगता है, हालांकि मुझे लगता है कि आदर्श जवाब यह होगा कि ऐप्पल इसे बनाता है ताकि मैं जो करने की कोशिश कर रहा था वह आसानी से प्रवाह कर सकता है चाहे वह पहला या दूसरा प्रोटोकॉल हो। यह मेरे लिए सहज महसूस करता है। मैं कोको देव मेलिंग सूची पर इस मामले के बारे में भी पूछ रहा था। आपके उत्तर के साथ और मैं वहां से क्या मिला, मैं इस मामले को बंद मानता हूं। धन्यवाद ... – xBACP

+0

@PartiallyFrozenOJ मदद करने के लिए खुश! – dfri

+1

@dfri - इस स्पष्टीकरण में इस मुद्दे के इतने सारे पहलुओं को इतनी स्पष्ट रूप से शामिल किया गया है कि मैं इसे अपने हाथ के पीछे मुद्रित करने का लुत्फ उठा रहा हूं। – Zhora

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