如何在Swift中扩展types化数组?

如何扩展Swift的Array<T>T[]types与自定义functionutils?

浏览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(){} } 

它没有build立与错误:

名义typesT[]不能被扩展

使用完整types定义失败, Use of undefined type 'T' ,即:

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

Array<T : Any>Array<String>也失败。

奇怪的Swift让我扩展一个无types的数组:

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

它让我打电话给:

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

但是我不能创build一个适当的genericstypes扩展,因为types在stream过方法时似乎会丢失,例如试图用以下方法replaceSwift的内置filter :

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

但编译器将其视为无types,仍允许使用以下方式调用扩展:

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

而且,当用debugging器直通时,表明这个types是Swift.String但是它是一个构build错误,试图访问它就像一个string,而不是先将其强制转换为String ,即:

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

有没有人知道什么是正确的方式来创build一个类似于内置扩展的types扩展方法?

对于使用类来扩展types化数组,下面的代码适用于我(Swift 2.2 )。 例如,对一个types数组进行sorting:

 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' 

更新

要扩展带有非类的types数组,请使用以下方法:

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

Swift 3中,一些types已被重命名:

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

经过一段时间尝试不同的东西,解决scheme似乎从签名中删除<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 } 

我有一个类似的问题 – 想用一个swap()方法来扩展通用数组,这个方法应该是和数组相同的参数。 但是,你如何指定generics? 我发现通过试验和错误,下面的工作:

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

关键是“元素”这个词。 请注意,我没有在任何地方定义这个types,它似乎自动存在于数组扩展的上下文中,并引用数组元素的types。

我不是100%肯定那里发生了什么,但我认为这可能是因为“元素”是数组的关联types(请参阅“关联types”在这里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)中的任何引用/结构/ S:萨 )…所以我还是有点不确定。

使用Swift 2.2 :当试图从string数组中删除重复项时遇到了类似的问题。 我能够在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类允许我调用数组上的两个方法之一,并成功删除重复项。 请注意,arrays中的元素必须符合Hashable协议。 现在我可以做到这一点:

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

如果你想了解在这个github的回购https://github.com/ankurp/Cent中扩展数组和类的构build类签出代码;

从Xcode 6.1开始,扩展数组的语法如下所示

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

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

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

它不是Array的扩展,而是CollectionType,所以同样的方法适用于其他集合types。 @noescape意味着传入的块不会离开过滤函数的范围,从而启用一些优化。 自我与资本S是我们正在延伸的类。 Self.Generator是一个遍历集合中的对象的迭代器,Self.Generator.Element是对象的types,例如数组[Int?] Self.Generator.Element是Int ?.

总而言之,这个过滤方法可以应用于任何CollectionType,它需要一个过滤块,它接受一个集合的元素并返回一个Bool,并返回一个原始types的数组。 所以把它们放在一起,这里有一个我认为有用的方法:它将map和filter结合起来,通过一个将collection元素映射到一个可选值的块,并返回一个不为零的可选值的数组。

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

Swift 2.x

您还可以扩展数组以符合通用types方法的包含blue-rpints的协议,例如,包含所有符合某种types约束的generics数组元素的自定义函数utils的协议,比如协议MyTypes 。 使用这种方法的好处是你可以编写采用通用数组参数的函数,并且有一个约束,即这些数组参数必须符合你的自定义函数实用程序协议,比如说协议MyFunctionalUtils

你可以隐式地获得这种行为,通过将数组元素限制为MyTypes ,或者如我将在下面描述的方法中所显示的那样—明显地让MyTypes数组函数头直接显示input数组符合MyFunctionalUtils


我们从Protocols MyTypes开始,用作types约束; 通过这个协议扩展你想要适合你的generics的types(下面的例子扩展了基本typesIntDouble以及自定义typesMyCustomType

 /* 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) } 

Protocol MyFunctionalUtils (持有蓝图我们的额外的generics数组函数实用程序),此后,通过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 } } 

最后,testing和两个例子显示了一个函数采用generics数组,分别有以下几种情况

  1. 通过将数组元素限制为“MyTypes”(函数bar1 ),显示数组参数符合协议“MyFunctionalUtils”的隐式断言。

  2. 显式地显示数组参数符合协议'MyFunctionalUtils'(functionbar2 )。

testing和例子如下:

 /* 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] } } 
 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 } } }