我如何能在Swift扩展类型化数组?

如何用自定义函数utils扩展Swift的Array<T>T[]类型?

浏览Swift的API文档可以发现数组方法是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一样访问它而不首先将其转换为String是一个构建错误,即:

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

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

109063 次浏览

经过一段时间的尝试,解决方案似乎从签名中删除了<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 }

如果你想了解扩展数组和其他类型的内置类的签出代码在这个github repo 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是我们要扩展的类。自我。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 {


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)。例如,对类型化数组排序:

class HighScoreEntry {
let score:Int
}


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

尝试使用结构体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 }
}
}

斯威夫特3中,一些类型被重命名:

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

我有一个类似的问题——想用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)中看不到任何对此的引用…所以我还是有点不确定。

(< >强劲迅速2. x < / >强)

你也可以扩展数组,使之符合包含泛型类型方法blue-rpints的协议,例如,包含符合某种类型约束的所有泛型数组元素的自定义函数utils的协议,比如协议MyTypes。使用这种方法的好处是,你可以编写带有泛型数组参数的函数,约束这些数组参数必须符合你的自定义函数实用程序协议,比如协议MyFunctionalUtils

你可以隐式地获得这种行为,通过类型约束数组元素为MyTypes,或者——正如我将在下面描述的方法中展示的那样——非常简洁、显式地让你的泛型数组函数头直接显示输入数组符合MyFunctionalUtils


我们从协议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;蓝印方法的实施:

/* 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
import Foundation


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

扩展所有类型:

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

扩展类似的类型:

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

扩展一些类型:

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

扩展特定的类型:

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

扩展数组查找索引:

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
}