我如何扩展Swift的数组<T>或T[]类型与自定义功能utils?

浏览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[]不能扩展

使用完整的类型定义在使用未定义类型'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)

但是我不能创建一个适当的泛型类型扩展,因为类型似乎在它流经方法时丢失了,例如试图用:

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。字符串,但这是一个构建错误,试图访问它像一个字符串,而不首先将其转换为字符串,即:

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

有人知道创建类似内置扩展的类型化扩展方法的正确方法吗?


当前回答

我看了一下Swift 2标准库的头文件,这里是过滤器函数的原型,这使得如何滚动您自己的头文件变得非常明显。

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

它不是Array的扩展,而是CollectionType的扩展,所以同样的方法也适用于其他集合类型。@noescape意味着传入的块不会离开过滤器函数的作用域,这使得一些优化成为可能。大写S的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
    }
}

其他回答

我有一个类似的问题——想用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)。例如,对类型化数组排序:

class HighScoreEntry {
    let score:Int
}

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

尝试用struct或typealias这样做将会给出一个错误:

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 
{
    // ...
}
import Foundation

extension Array {
    var randomItem: Element? {
        let idx = Int(arc4random_uniform(UInt32(self.count)))
        return self.isEmpty ? nil : self[idx]
    }
}
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
        }

    }

}

使用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"]