मैं स्विफ्ट में टाइप किए गए एरे को कैसे बढ़ा सकता हूं?


203

मैं कैसे स्विफ्ट का विस्तार कर सकते हैं Array<T>या T[]कस्टम कार्यात्मक utils के साथ प्रकार?

स्विफ्ट के एपीआई डॉक्स के आस-पास ब्राउज़ करने से पता चलता है कि एरे तरीकों का विस्तार है T[], जैसे:

extension T[] : ArrayType {
    //...
    init()

    var count: Int { get }

    var capacity: Int { get }

    var isEmpty: Bool { get }

    func copy() -> T[]
}

एक ही स्रोत को कॉपी और पेस्ट करते समय और किसी भी बदलाव की कोशिश करना जैसे:

extension T[] : ArrayType {
    func foo(){}
}

extension T[] {
    func foo(){}
}

यह त्रुटि के साथ निर्मित करने में विफल रहता है:

नाममात्र प्रकार T[]बढ़ाया नहीं जा सकता

पूर्ण प्रकार की परिभाषा का उपयोग करना विफल रहता है Use of undefined type 'T', अर्थात:

extension Array<T> {
    func foo(){}
}

और यह भी साथ में विफल रहता है Array<T : Any>और Array<String>

उत्सुकता से स्विफ्ट मुझे एक अनपेक्षित सरणी का विस्तार करने देता है:

extension Array {
    func each(fn: (Any) -> ()) {
        for i in self {
            fn(i)
        }
    }
}

जो मुझे इसके साथ कॉल करने देता है:

[1,2,3].each(println)

लेकिन मैं एक उचित सामान्य प्रकार का एक्सटेंशन नहीं बना सकता हूं क्योंकि यह विधि के माध्यम से प्रवाहित होने पर प्रकार खो जाता है, जैसे कि स्विफ्ट के अंतर्निहित फ़िल्टर को बदलने की कोशिश कर रहा है :

extension Array {
    func find<T>(fn: (T) -> Bool) -> T[] {
        var to = T[]()
        for x in self {
            let t = x as T
            if fn(t) {
                to += t
            }
        }
        return to
    }
}

लेकिन कंपाइलर इसे अप्रतिबंधित मानता है जहां यह अभी भी एक्सटेंशन को कॉल करने की अनुमति देता है:

["A","B","C"].find { $0 > "A" }

और जब डिबगर के साथ चरण-थ्रू इंगित करता है कि प्रकार है, Swift.Stringलेकिन यह Stringपहली बार, यानी इसे कास्टिंग के बिना इसे स्ट्रिंग की तरह एक्सेस करने का प्रयास करने के लिए एक बिल्ड त्रुटि है :

["A","B","C"].find { ($0 as String).compare("A") > 0 }

क्या किसी को पता है कि टाइप एक्सटेंशन एक्सटेंशन बनाने का उचित तरीका क्या है जो बिल्ट-इन एक्सटेंशन की तरह काम करता है?


वोट दिया क्योंकि मुझे खुद भी कोई जवाब नहीं मिला। extension T[]XCode में Array टाइप पर कमांड-क्लिक करते समय समान रूप से देखना , लेकिन त्रुटि प्राप्त किए बिना इसे लागू करने का कोई तरीका नहीं देखना।
उपयोगकर्ता नाम tbd

@usernametbd FYI अभी मिला, ऐसा लगता <T>है कि विधि हस्ताक्षर से हटाने के लिए समाधान था ।
मिथ्ज

जवाबों:


296

कक्षाओं के साथ टाइप किए गए सरणियों के विस्तार के लिए , नीचे मेरे लिए काम करता है (स्विफ्ट 2.2 )। उदाहरण के लिए, टाइप किया गया सरणी छाँटना:

class HighScoreEntry {
    let score:Int
}

extension Array where Element == HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { $0.score < $1.score }
    }
}

एक संरचना या टाइपेलियास के साथ ऐसा करने की कोशिश करना एक त्रुटि देगा:

Type 'Element' constrained to a non-protocol type 'HighScoreEntry'

अपडेट :

गैर-कक्षाओं के साथ टाइप किए गए सरणियों का विस्तार करने के लिए निम्नलिखित दृष्टिकोण का उपयोग करें:

typealias HighScoreEntry = (Int)

extension SequenceType where Generator.Element == HighScoreEntry {
    func sort() -> [HighScoreEntry] {
      return sort { $0 < $1 }
    }
}

में स्विफ्ट 3 कुछ प्रकार के नाम दिया गया है:

extension Sequence where Iterator.Element == HighScoreEntry 
{
    // ...
}

1
संकलक रिपोर्ट करता है कि 'अनुक्रम' का नाम बदलकर 'अनुक्रम' कर दिया गया है
सैंडोवर

आपने रिटर्न प्रकार में Iterator.Element का उपयोग क्यों नहीं किया [Iterator.Element]?
गॉसब्लाउर्स्की

1
नमस्ते, क्या आप 4.1 में सशर्त अनुरूप सुविधा की व्याख्या कर सकते हैं? 4.1 में नया क्या है? हम 2.2 में ऐसा कर सकते हैं? मुझे क्या याद आ रहा है
osrl

स्विफ्ट 3.1 के बाद से आप निम्न सिंटैक्स के साथ गैर-कक्षाओं के साथ सरणियों का विस्तार कर सकते हैं: एरे जहां एलीमेंट == Int
Giles

63

कुछ समय बाद अलग-अलग चीजों को आजमाने से समाधान <T>को हस्ताक्षर से हटा दिया जाता है जैसे:

extension Array {
    func find(fn: (T) -> Bool) -> [T] {
        var to = [T]()
        for x in self {
            let t = x as T;
            if fn(t) {
                to += t
            }
        }
        return to
    }
}

जो अब बिना बिल्ड एरर के काम करता है:

["A","B","C"].find { $0.compare("A") > 0 }

1
BTW क्या आप यहाँ परिभाषित किया गया है कार्यात्मक रूप से मौजूदा filterफ़ंक्शन के बराबर है :let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
Palimondo


4
समझा। डबल छानने बल्कि मेरे लिए गाड़ी लगता है ... लेकिन यह अभी भी मानती है कि filterहै कार्यात्मक रूप से बराबर अपने को find, यानी फ़ंक्शन के परिणाम एक ही है। यदि आपके फ़िल्टर बंद होने के साइड-इफ़ेक्ट हैं, तो आप निश्चित रूप से परिणाम पसंद नहीं कर सकते हैं।
पालीमोंडो

2
@Palimondo बिल्कुल सही है, डिफ़ॉल्ट फ़िल्टर में अप्रत्याशित व्यवहार होता है जबकि ऊपर वाला इम्प्लांट अपेक्षा के अनुसार काम करता है (और यह क्यों मौजूद है)। यह कार्यात्मक रूप से समतुल्य नहीं है यदि यह दो बार क्लोजर को निष्पादित करता है, जो संभावित रूप से स्कॉप्ड वैरिएबल को म्यूट कर सकता है (जो कि बग मैं भाग गया था, इसलिए इसके व्यवहार पर सवाल है)। इस सवाल पर भी विशेष रूप से ध्यान दें कि स्विफ्ट के बिल्ट-इन को बदलने की इच्छा है filter
मिथक

4
हम कार्यात्मक शब्द की परिभाषा पर बहस कर रहे हैं । प्रथानुसार, कार्यात्मक प्रोग्रामिंग प्रतिमान जहां में filter, mapऔर reduceकार्यों से ही शुरू, कार्यों उनकी वापसी मूल्यों के लिए क्रियान्वित कर रहे हैं। इसके विपरीत, eachआप जिस फ़ंक्शन को ऊपर परिभाषित करते हैं , वह इसके साइड-इफेक्ट के लिए निष्पादित फ़ंक्शन का एक उदाहरण है, क्योंकि यह कुछ भी नहीं देता है। मुझे लगता है कि हम सहमत हो सकते हैं कि वर्तमान स्विफ्ट कार्यान्वयन आदर्श नहीं है और दस्तावेज़ीकरण इसकी रनटाइम विशेषताओं के बारे में कुछ भी नहीं बताता है।
पालिमोंडो

24

सभी प्रकार बढ़ाएँ :

extension Array where Element: Comparable {
    // ...
}

कुछ प्रकार बढ़ाएँ :

extension Array where Element: Comparable & Hashable {
    // ...
}

एक विशेष प्रकार का विस्तार करें :

extension Array where Element == Int {
    // ...
}

8

मुझे एक समान समस्या थी - सामान्य एरे को एक स्वैप () विधि के साथ विस्तारित करना चाहता था, जो कि सरणी के समान प्रकार का एक तर्क लेने वाला था। लेकिन आप सामान्य प्रकार को कैसे निर्दिष्ट करते हैं? मैंने परीक्षण और त्रुटि से पाया कि नीचे काम किया:

extension Array {
    mutating func swap(x:[Element]) {
        self.removeAll()
        self.appendContentsOf(x)
    }
}

इसकी कुंजी 'एलिमेंट' शब्द था। ध्यान दें कि मैंने कहीं भी इस प्रकार को परिभाषित नहीं किया है, यह सरणी विस्तार के संदर्भ में स्वचालित रूप से मौजूद है, और सरणी के तत्वों का प्रकार जो भी है उसे देखें।

मुझे यकीन नहीं है कि वहां क्या चल रहा है, लेकिन मुझे लगता है कि शायद यह इसलिए है क्योंकि 'एलीमेंट' एरे का एक संबद्ध प्रकार है (देखें 'एसोसिएटेड टाइप्स' यहां https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )

हालाँकि, मैं इसका कोई संदर्भ Array संरचना संदर्भ ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Reference/Swift_Array_Structure/index.html#//apple_ref/swift) में नहीं देख सकता / संरचना / एस: सा ) ... तो मैं अभी भी थोड़ा अनिश्चित हूं।


1
Arrayएक सामान्य प्रकार है: Array<Element>( swiftdoc.org/v2.1/type/Array देखें ), Elementनिहित प्रकार के लिए एक प्लेसहोल्डर है। उदाहरण के लिए: var myArray = [Foo]()इसका मतलब है कि myArrayकेवल प्रकार शामिल होंगे FooFooइस मामले में जेनेरिक प्लेसहोल्डर को "मैप" किया जाता है Element। यदि आप ऐरे के सामान्य व्यवहार को बदलना चाहते हैं (विस्तार के माध्यम से) तो आप सामान्य प्लेसहोल्डर का उपयोग करेंगे Elementऔर किसी भी प्रकार का ठोस (जैसे जू) नहीं।
डेविड जेम्स

5

स्विफ्ट 2.2 का उपयोग करना : मैं एक समान मुद्दे में भाग गया जब डुप्लिकेट को स्ट्रिंग्स के एक सरणी से निकालने की कोशिश कर रहा था। मैं ऐरे वर्ग पर एक विस्तार जोड़ने में सक्षम था जो कि बस वही करता था जो मैं करना चाहता था।

extension Array where Element: Hashable {
    /**
     * Remove duplicate elements from an array
     *
     * - returns: A new array without duplicates
     */
    func removeDuplicates() -> [Element] {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        return result
    }

    /**
     * Remove duplicate elements from an array
     */
    mutating func removeDuplicatesInPlace() {
        var result: [Element] = []
        for value in self {
            if !result.contains(value) {
                result.append(value)
            }
        }
        self = result
    }
}

इन दो विधियों को ऐरे वर्ग में जोड़ने से मुझे दो तरीकों में से किसी एक सरणी पर कॉल करने और डुप्लिकेट को सफलतापूर्वक निकालने की अनुमति मिलती है। ध्यान दें कि एरे में तत्व हस्सिबल प्रोटोकॉल के अनुरूप होने चाहिए। अब मैं यह कर सकता हूं:

 var dupes = ["one", "two", "two", "three"]
 let deDuped = dupes.removeDuplicates()
 dupes.removeDuplicatesInPlace()
 // result: ["one", "two", "three"]

यह भी पूरा किया जा सकता है let deDuped = Set(dupes), जिसे आप एक गैर-विनाशकारी विधि में वापस ला सकते हैं, toSetजब तक कि आप प्रकार परिवर्तन के साथ ठीक न हों
एलेक्सपींग

@alexpyoung अगर आप सेट ()
डैनी वैंग

5

यदि आप इस github रेपो https://github.com/ankurp/Cent में क्लासेस चेकआउट कोड में एरे और अन्य प्रकार के निर्माण के बारे में जानना चाहते हैं

Xcode 6.1 के रूप में सरणियों का विस्तार करने के लिए सिंटैक्स निम्नानुसार है

extension Array {
    func at(indexes: Int...) -> [Element] {
        ... // You code goes herer
    }
}

1
@Rob URL को अपडेट करें
पीटीएल

3

मैं स्विफ्ट 2 मानक लाइब्रेरी हेडर पर एक नज़र रखता था, और यहाँ फिल्टर फ़ंक्शन के लिए प्रोटोटाइप है, जो यह स्पष्ट करता है कि अपने स्वयं के रोल कैसे करें।

extension CollectionType {
    func filter(@noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
}

यह Array का विस्तार नहीं है, लेकिन CollectionType के लिए है, इसलिए अन्य संग्रह प्रकारों के लिए भी यही तरीका लागू होता है। @noescape का अर्थ है कि पास किया गया ब्लॉक फ़िल्टर फ़ंक्शन के दायरे को नहीं छोड़ेगा, जो कुछ अनुकूलन को सक्षम करता है। एक पूंजी एस के साथ स्व हम जिस वर्ग का विस्तार कर रहे हैं। Self.Generator एक पुनरावृत्ति है जो संग्रह में वस्तुओं के माध्यम से पुनरावृत्ति करता है और Self.Generator.Element वस्तुओं का प्रकार है, उदाहरण के लिए एक सरणी [Int?] Self.Generator.Element Int होगा?

इस सभी फ़िल्टर विधि को किसी भी CollectionType पर लागू किया जा सकता है, इसे एक फ़िल्टर ब्लॉक की आवश्यकता होती है जो संग्रह का एक तत्व लेता है और एक Bool देता है, और यह मूल प्रकार की एक सरणी देता है। तो इसे एक साथ रखकर, यहां एक तरीका है जो मुझे उपयोगी लगता है: यह मैप और फिल्टर को जोड़ती है, एक ब्लॉक लेती है जो संग्रह तत्व को एक वैकल्पिक मूल्य पर मैप करता है, और उन वैकल्पिक मानों की एक सरणी देता है जो शून्य नहीं हैं।

extension CollectionType {

    func mapfilter<T>(@noescape transform: (Self.Generator.Element) -> T?) -> [T] {
        var result: [T] = []
        for x in self {
            if let t = transform (x) {
                result.append (t)
            }
        }
        return result
    }
}


0

( स्विफ्ट 2.x )

आप प्रोटोकॉल को भी कह सकते हैं कि जेनेरिक प्रकार के तरीकों के लिए ब्लू-रिपिंट वाले प्रोटोकॉल के अनुरूप सरणी का विस्तार कर सकते हैं, उदाहरण के लिए, एक प्रोटोकॉल जिसमें सभी प्रकार के जेनेरिक सरणी तत्वों के लिए कुछ प्रकार की बाधा के अनुरूप आपके कस्टम कार्यात्मक बर्तन हैं, प्रोटोकॉल कहते हैं MyTypes। इस दृष्टिकोण का उपयोग करने वाला बोनस यह है कि आप सामान्य सरणी तर्कों को लेते हुए कार्य लिख सकते हैं, एक बाधा के साथ कि ये सरणी तर्क आपके कस्टम फ़ंक्शन उपयोगिताओं प्रोटोकॉल के अनुरूप होने चाहिए, प्रोटोकॉल कहते हैं MyFunctionalUtils

आप इस व्यवहार को या तो स्पष्ट रूप से प्राप्त कर सकते हैं, प्रकार सरणी तत्वों को विवश करके MyTypes, या --- जैसा कि मैं नीचे वर्णित विधि में दिखाऊंगा ---, काफी करीने से, स्पष्ट रूप से, अपने सामान्य सरणी फ़ंक्शंस हेडर को सीधे इनपुट एरे को दिखाते हुए। के अनुरूप है MyFunctionalUtils


हम MyTypesप्रकार की बाधा के रूप में उपयोग के लिए प्रोटोकॉल के साथ शुरू करते हैं ; इस प्रोटोकॉल द्वारा आपके जेनेरिक में फिट होने के प्रकारों का विस्तार करें (नीचे दिए गए उदाहरण मौलिक प्रकार Intऔर Doubleसाथ ही एक कस्टम प्रकार का विस्तार करते हैं MyCustomType)

/* Used as type constraint for Generator.Element */
protocol MyTypes {
    var intValue: Int { get }
    init(_ value: Int)
    func *(lhs: Self, rhs: Self) -> Self
    func +=(inout lhs: Self, rhs: Self)
}

extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
    // ...

/* Custom type conforming to MyTypes type constraint */
struct MyCustomType : MyTypes {
    var myInt : Int? = 0
    var intValue: Int {
        return myInt ?? 0
    }

    init(_ value: Int) {
        myInt = value
    }
}

func *(lhs: MyCustomType, rhs: MyCustomType) -> MyCustomType {
    return MyCustomType(lhs.intValue * rhs.intValue)
}

func +=(inout lhs: MyCustomType, rhs: MyCustomType) {
    lhs.myInt = (lhs.myInt ?? 0) + (rhs.myInt ?? 0)
}

प्रोटोकॉल MyFunctionalUtils(ब्लूप्रिन्ट्स हमारे अतिरिक्त जेनेरिक सरणी फ़ंक्शन उपयोगिताओं को पकड़कर) और उसके बाद एरे के विस्तार MyFunctionalUtils; ब्लू-प्रिंटेड विधि का कार्यान्वयन:

/* Protocol holding our function utilities, to be used as extension 
   o Array: blueprints for utility methods where Generator.Element 
   is constrained to MyTypes */
protocol MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int?
        // ...
}

/* Extend array by protocol MyFunctionalUtils and implement blue-prints 
   therein for conformance */
extension Array : MyFunctionalUtils {
    func foo<T: MyTypes>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

अंत में, परीक्षण और दो उदाहरणों में क्रमशः निम्न मामलों के साथ, जेनेरिक सरणियों को लेते हुए एक फ़ंक्शन दिखाया गया है

  1. अंतर्निहित अभिकथन दिखा रहा है कि सरणी पैरामीटर प्रोटोकॉल 'MyFunctionalUtils' के अनुरूप है, प्रकार के तत्वों को 'MyTypes' (फ़ंक्शन bar1) के लिए विवश करने के माध्यम से ।

  2. स्पष्ट रूप से दिखा रहा है कि सरणी पैरामीटर प्रोटोकॉल 'MyFunctionalUtils' (फ़ंक्शन bar2) के अनुरूप हैं ।

परीक्षण और उदाहरण इस प्रकार हैं:

/* Tests & examples */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1my : [MyCustomType] = [MyCustomType(1), MyCustomType(2), MyCustomType(3)]
let arr2my : [MyCustomType] = [MyCustomType(-3), MyCustomType(-2), MyCustomType(1)]

    /* constrain array elements to MyTypes, hence _implicitly_ constraining
       array parameters to protocol MyFunctionalUtils. However, this
       conformance is not apparent just by looking at the function signature... */
func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK
let myInt1my = bar1(arr1my, arr2my) // -4, OK

    /* constrain the array itself to protocol MyFunctionalUtils; here, we
       see directly in the function signature that conformance to
       MyFunctionalUtils is given for valid array parameters */
func bar2<T: MyTypes, U: protocol<MyFunctionalUtils, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {

    // OK, type U behaves as array type with elements T (=MyTypes)
    var a = arr1
    var b = arr2
    a.append(T(2)) // add 2*7 to multsum
    b.append(T(7))

    return a.foo(Array(b))
        /* Ok! */
}
let myInt2d = bar2(arr1d, arr2d) // 10, OK
let myInt2my = bar2(arr1my, arr2my) // 10, OK

-1
import Foundation

extension Array {

    func calculateMean() -> Double {
        // is this an array of Doubles?
        if self.first is Double {
            // cast from "generic" array to typed array of Doubles
            let doubleArray = self.map { $0 as! Double }

            // use Swift "reduce" function to add all values together
            let total = doubleArray.reduce(0.0, combine: {$0 + $1})

            let meanAvg = total / Double(self.count)
            return meanAvg

        } else {
            return Double.NaN
        }
    }

    func calculateMedian() -> Double {
        // is this an array of Doubles?
        if self.first is Double {
            // cast from "generic" array to typed array of Doubles
            var doubleArray = self.map { $0 as! Double }

            // sort the array
            doubleArray.sort( {$0 < $1} )

            var medianAvg : Double
            if doubleArray.count % 2 == 0 {
                // if even number of elements - then mean average the middle two elements
                var halfway = doubleArray.count / 2
                medianAvg = (doubleArray[halfway] + doubleArray[halfway - 1]) / 2

            } else {
                // odd number of elements - then just use the middle element
                medianAvg = doubleArray[doubleArray.count  / 2 ]
            }
            return medianAvg
        } else {
            return Double.NaN
        }

    }

}

2
ये डाउनकास्ट ( $0 as! Double) स्विफ्ट के प्रकार प्रणाली के खिलाफ लड़ रहे हैं और ओपी के प्रश्न के उद्देश्य को भी मेरी राय में पराजित करते हैं। ऐसा करके कि आप वास्तव में जो गणना करना चाहते हैं, उसके लिए कंपाइलर ऑप्टिमाइजेशन के लिए आप किसी भी संभावना को खो रहे हैं, और आप अरिव के नाम स्थान को भी अर्थहीन कार्यों के साथ प्रदूषित कर रहे हैं (आप UIViews की एक सरणी में .calculateMedian () क्यों देखना चाहते हैं, या कर सकते हैं)। कुछ भी लेकिन उस मामले के लिए डबल?)। एक बेहतर रास्ता है।
एपीर

कोशिशextension CollectionType where Generator.Element == Double {}
पंचक 18
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.