¿Cómo puedo extender matrices mecanografiadas en Swift?

¿Cómo puedo extender el tipo Array o T[] Swift con utilidades funcionales personalizadas?

Al examinar los documentos API de Swift, se muestra que los métodos de Array son una extensión de T[] , por ejemplo:

 extension T[] : ArrayType { //... init() var count: Int { get } var capacity: Int { get } var isEmpty: Bool { get } func copy() -> T[] } 

Al copiar y pegar la misma fuente e intentar cualquier variación como:

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

No se puede construir con el error:

El tipo nominal T[] no puede extenderse

El uso de la definición de tipo completo falla con el Use of undefined type 'T' , es decir:

 extension Array { func foo(){} } 

Y también falla con Array y Array .

Curiosamente Swift me permite extender una matriz sin tipo con:

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

Lo cual me permite llamar con:

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

Pero no puedo crear una extensión de tipo genérica adecuada ya que el tipo parece estar perdido cuando fluye a través del método, por ejemplo, tratando de reemplazar el filtro incorporado de Swift con :

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

Pero el comstackdor lo trata como sin tipo donde todavía permite llamar a la extensión con:

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

Y cuando se Swift.String con un depurador indica que el tipo es Swift.String pero es un error de comstackción intentar acceder a él como una cadena sin convertirlo primero en String , es decir:

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

¿Alguien sabe cuál es la forma correcta de crear un método de extensión tipeado que actúe como las extensiones integradas?

Para extender matrices tipadas con clases , lo siguiente funciona para mí (Swift 2.2 ). Por ejemplo, clasificando una matriz tipada:

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

Intentar hacer esto con una struct o typealias dará un error:

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

Actualización :

Para extender matrices tipadas con no clases, use el siguiente enfoque:

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

En Swift 3 algunos tipos han sido renombrados:

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

Después de un tiempo intentando cosas diferentes, la solución parece eliminar la de la firma como:

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

Que ahora funciona como se pretendía sin errores de comstackción:

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

Tuve un problema similar: quería extender la matriz general con un método swap (), que se suponía que tomaría un argumento del mismo tipo que la matriz. Pero, ¿cómo se especifica el tipo genérico? Encontré por prueba y error que el siguiente trabajó:

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

La clave fue la palabra ‘Elemento’. Tenga en cuenta que no definí este tipo en ningún lugar, parece existir automáticamente dentro del contexto de la extensión de matriz, y me refiero a cualquier tipo de los elementos de la matriz.

No estoy 100% seguro de lo que está sucediendo allí, pero creo que es probablemente porque ‘Element’ es un tipo asociado de la matriz (consulte ‘Tipos asociados’ aquí https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )

Sin embargo, no puedo ver ninguna referencia de esto en la referencia de la estructura Array ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) … así que todavía estoy un poco inseguro.

Usando Swift 2.2 : me encontré con un problema similar cuando trataba de eliminar duplicados de una matriz de cadenas. Pude agregar una extensión en la clase Array que hace exactamente lo que estaba buscando hacer.

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

Agregar estos dos métodos a la clase Array me permite llamar a uno de los dos métodos en una matriz y eliminar correctamente los duplicados. Tenga en cuenta que los elementos en la matriz deben ajustarse al protocolo Hashable. Ahora puedo hacer esto:

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

Eché un vistazo a los encabezados de biblioteca estándar de Swift 2, y aquí está el prototipo de la función de filtro, que hace que sea bastante obvio cómo hacer la tuya.

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

No es una extensión de Array, sino de CollectionType, por lo que el mismo método se aplica a otros tipos de colecciones. @noescape significa que el bloque pasado no saldrá del scope de la función de filtro, lo que permite algunas optimizaciones. El yo con una S mayúscula es la clase que estamos extendiendo. Self.Generator es un iterador que recorre los objetos en la colección y Self.Generator.Element es el tipo de los objetos, por ejemplo para una matriz [Int?] Self.Generator.Element sería Int.

En general, este método de filtro se puede aplicar a cualquier CollectionType, necesita un bloque de filtro que toma un elemento de la colección y devuelve un Bool, y devuelve una matriz del tipo original. Así que juntando esto, he aquí un método que considero útil: combina mapa y filtro, tomando un bloque que asigna un elemento de colección a un valor opcional, y devuelve una matriz de esos valores opcionales que no son nada.

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

Si desea obtener más información sobre la extensión de matrices y otros tipos de comstackción en el código de pago de clases en este repository github https://github.com/ankurp/Cent

A partir de Xcode 6.1, la syntax para extender matrices es la siguiente

 extension Array { func at(indexes: Int...) -> [Element] { ... // You code goes herer } } 
 import Foundation extension Array { var randomItem: Element? { let idx = Int(arc4random_uniform(UInt32(self.count))) return self.isEmpty ? nil : self[idx] } } 

( Swift 2.x )

También puede ampliar la matriz para cumplir con un protocolo que contenga blue-rpints para métodos de tipo genérico, por ejemplo, un protocolo que contenga sus utilidades funcionales personalizadas para todos los elementos de matriz generics que se ajusten a alguna restricción de tipo, por ejemplo, el protocolo MyTypes . La ventaja de usar este enfoque es que puede escribir funciones tomando argumentos de matriz generics, con la restricción de que estos argumentos de matriz deben ajustarse a su protocolo de utilidades de función personalizado, por ejemplo, el protocolo MyFunctionalUtils .

Puede obtener este comportamiento de forma implícita, mediante el tipo que restringe los elementos de la matriz a MyTypes , o — como mostraré en el método que describo a continuación —, bastante clara y explícitamente, dejando que el encabezado genérico de funciones de matriz muestre directamente esa entrada arrays se ajusta a MyFunctionalUtils .


Comenzamos con los Protocolos MyTypes para usarlos como restricción de tipo; extienda los tipos que desea encajar en sus generics mediante este protocolo (el ejemplo siguiente amplía los tipos fundamentales Int y Double así como un tipo personalizado 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) } 

Protocolo MyFunctionalUtils (que contiene planos de nuestras funciones genéricas adicionales de funciones de matriz) y, posteriormente, la extensión de Array by MyFunctionalUtils ; implementación de método (s) impresos en azul:

 /* 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(a: [T]) -> Int? // ... } /* Extend array by protocol MyFunctionalUtils and implement blue-prints therein for conformance */ extension Array : MyFunctionalUtils { func foo(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 } } 

Finalmente, las pruebas y dos ejemplos que muestran una función tomando matrices genéricas, con los siguientes casos, respectivamente

  1. Se muestra la afirmación implícita de que los parámetros de la matriz se ajustan al protocolo ‘MyFunctionalUtils’, a través del tipo que restringe los elementos de arrays a ‘MyTypes’ (función bar1 ).

  2. Mostrar explícitamente que los parámetros de la matriz se ajustan al protocolo ‘MyFunctionalUtils’ (función bar2 ).

La prueba y ejemplos a continuación:

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