簡體   English   中英

如何在 Swift 中擴展鍵入的 Arrays?

[英]How can I extend typed Arrays in Swift?

如何使用自定義功能實用程序擴展 Swift 的Array<T>T[]類型?

瀏覽 Swift 的 API 文檔表明 Array 方法是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>而失敗。

奇怪的是 Swift 讓我擴展了一個無類型數組:

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

它讓我打電話給:

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

但是我無法創建適當的泛型類型擴展,因為類型在流經該方法時似乎丟失了,例如嘗試將 Swift 的內置過濾器替換為

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 }

有誰知道創建類似於內置擴展的類型化擴展方法的正確方法是什么?

對於使用classes擴展類型化數組,以下適用於我(Swift 2.2 )。 例如,對類型化數組進行排序:

class HighScoreEntry {
    let score:Int
}

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

嘗試使用structtypealias執行此操作會產生錯誤:

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 }
    }
}

Swift 3 中,某些類型已被重命名:

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

經過一段時間嘗試不同的東西后,解決方案似乎從簽名中刪除了<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 }

擴展所有類型:

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

擴展比較類型:

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

擴展一些類型:

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

擴展特定類型:

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

我有一個類似的問題 - 想用 swap() 方法擴展通用數組,該方法應該采用與數組相同類型的參數。 但是如何指定泛型類型呢? 我通過反復試驗發現以下方法有效:

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

它的關鍵是“元素”這個詞。 請注意,我沒有在任何地方定義此類型,它似乎自動存在於數組擴展的上下文中,並引用數組元素的任何類型。

我不是 100% 確定那里發生了什么,但我認為這可能是因為“元素”是數組的關聯類型(請參閱此處的“關聯類型” https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )

但是,我在數組結構參考( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift /struct/s:Sa )...所以我還是有點不確定。

使用 Swift 2.2 :我在嘗試從字符串數組中刪除重復項時遇到了類似的問題。 我能夠在 Array 類上添加一個擴展,它正是我想要做的。

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
    }
}

將這兩個方法添加到 Array 類允許我在數組上調用這兩個方法之一並成功刪除重復項。 請注意,數組中的元素必須符合 Hashable 協議。 現在我可以這樣做:

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

如果您想了解擴展數組和其他類型的內置類,請查看此 github 存儲庫中的代碼https://github.com/ankurp/Cent

從 Xcode 6.1 開始,擴展數組的語法如下

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

我查看了 Swift 2 標准庫頭文件,這里是過濾器函數的原型,這使得如何滾動自己的函數變得非常明顯。

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

它不是對 Array 的擴展,而是對 CollectionType 的擴展,因此相同的方法適用於其他集合類型。 @noescape 意味着傳入的塊不會離開過濾器函數的范圍,這可以進行一些優化。 帶有大寫 S 的 Self 是我們要擴展的類。 Self.Generator 是一個迭代器,它遍歷集合中的對象,而 Self.Generator.Element 是對象的類型,例如對於數組 [Int?] Self.Generator.Element 將是 Int?。

總而言之,這個過濾器方法可以應用於任何 CollectionType,它需要一個過濾器塊,它接受集合的一個元素並返回一個 Bool,它返回一個原始類型的數組。 所以把它們放在一起,這是一個我覺得有用的方法:它結合了 map 和 filter,通過獲取一個將集合元素映射到可選值的塊,並返回一個包含非 nil 的可選值的數組。

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
    }
}
import Foundation

extension Array {
    var randomItem: Element? {
        let idx = Int(arc4random_uniform(UInt32(self.count)))
        return self.isEmpty ? nil : self[idx]
    }
}

(斯威夫特 2.x )

您還可以擴展數組以符合包含用於泛型類型方法的 blue-rpints 的協議,例如,包含您的自定義功能實用程序的協議,用於符合某種類型約束的所有泛型數組元素,例如協議MyTypes 使用這種方法的好處是您可以編寫采用通用數組參數的函數,但有一個約束,即這些數組參數必須符合您的自定義函數實用程序協議,例如協議MyFunctionalUtils

您可以通過將數組元素類型約束為MyTypes來隱式地獲得這種行為,或者——正如我將在下面描述的方法中展示的那樣——非常巧妙、明確地讓您的通用數組函數頭直接顯示該輸入數組符合MyFunctionalUtils


我們從用作類型約束的 Protocols MyTypes開始; 通過此協議擴展您想要適合泛型的類型(下面的示例擴展了基本類型IntDouble以及自定義類型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對 Array 的擴展; 藍圖方法的實施:

/* 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. 通過將數組元素的類型限制為“MyTypes”(函數bar1 ),顯示隱式斷言數組參數符合協議“MyFunctionalUtils”。

  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
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
        }

    }

}

擴展數組查找索引:

extension Array where Element: Equatable {
func findElementArrayIndex(findElement: String) -> Int {
    var indexValue: Int = 0
    var search = self.filter { findElement.isEmpty || "\($0)".contains(findElement)}
    //print("search: \(search)")
    for i in 0..<self.count {
        if self[i] == search[0] {
            indexValue = i
            break
        }
    }
    return indexValue
}

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM