¿Cómo puedo extender Swift Array<T>
o T[]
escribir con utilidades funcionales personalizadas?
Examinar los documentos API de Swift muestra que los métodos de matriz 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 y probar cualquier variación como:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
No se genera con el error:
El tipo nominal
T[]
no se puede extender
El uso de la definición de tipo completo falla Use of undefined type 'T'
, es decir:
extension Array<T> {
func foo(){}
}
Y también falla con Array<T : Any>
y Array<String>
.
Curiosamente, Swift me permite extender una matriz sin tipo con:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Con lo cual me deja llamar:
[1,2,3].each(println)
Pero no puedo crear una extensión de tipo genérico adecuada, ya que el tipo parece perderse cuando fluye a través del método, por ejemplo, tratando de reemplazar el filtro incorporado de Swift con :
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
}
}
Pero el compilador lo trata como sin tipo donde todavía permite llamar a la extensión con:
["A","B","C"].find { $0 > "A" }
Y cuando se pasa con un depurador indica que el tipo es Swift.String
pero es un error de compilación intentar acceder a él como una Cadena sin lanzarlo String
primero, 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 mecanografiado que actúa como las extensiones incorporadas?
extension T[]
bit cuando Comando-clic en el tipo Array en XCode, pero no ve ninguna forma de implementarlo sin obtener un error.<T>
de la firma del método.Respuestas:
Para extender matrices escritas con clases , lo siguiente funciona para mí (Swift 2.2 ). Por ejemplo, ordenando una matriz escrita:
Intentar hacer esto con una estructura o typealias dará un error:
Actualizar :
Para ampliar las matrices escritas con no clases, utilice el siguiente enfoque:
En Swift 3 algunos tipos han sido renombrados:
fuente
[Iterator.Element]
?Después de un tiempo probando cosas diferentes, la solución parece eliminar el
<T>
de la firma como:Que ahora funciona según lo previsto sin errores de compilación:
fuente
filter
función existente :let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
es funcionalmente equivalente a sufind
, es decir, el resultado de la función es el mismo. Si el cierre de su filtro tiene efectos secundarios, seguramente no le gusten los resultados.filter
.filter
,map
yreduce
las funciones se originan de, las funciones se ejecutan por sus valores de retorno. Por el contrario, laeach
función que define arriba es un ejemplo de una función ejecutada por su efecto secundario, porque no devuelve nada. Creo que podemos estar de acuerdo en que la implementación actual de Swift no es ideal y la documentación no indica nada sobre sus características de tiempo de ejecución.Extienda todos los tipos:
Extiende algunos tipos:
Extiende un tipo particular :
fuente
Tuve un problema similar: quería extender la matriz general con un método swap (), que se suponía que debía tomar un argumento del mismo tipo que la matriz. Pero, ¿cómo se especifica el tipo genérico? Descubrí por prueba y error que lo siguiente funcionó:
La clave fue la palabra 'Elemento'. Tenga en cuenta que no definí este tipo en ninguna parte, parece existir automáticamente dentro del contexto de la extensión de la matriz, y se refiere a cualquier tipo de elementos de la matriz.
No estoy 100% seguro de lo que está sucediendo allí, pero creo que probablemente se deba a que 'Elemento' 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.
fuente
Array
es un tipo genérico:Array<Element>
(ver swiftdoc.org/v2.1/type/Array ),Element
es un marcador de posición para el tipo contenido. Por ejemplo:var myArray = [Foo]()
significa quemyArray
solo contendrá tipoFoo
.Foo
en este caso se "asigna" al marcador de posición genéricoElement
. Si desea cambiar el comportamiento general de Array (a través de la extensión), usaría el marcador de posición genéricoElement
y no cualquier tipo concreto (como Foo).Usando Swift 2.2 : me encontré con un problema similar al intentar eliminar duplicados de una serie de cadenas. Pude agregar una extensión en la clase Array que hace exactamente lo que estaba buscando hacer.
Agregar estos dos métodos a la clase Array me permite llamar a uno de los dos métodos en una matriz y eliminar con éxito los duplicados. Tenga en cuenta que los elementos en la matriz deben cumplir con el protocolo Hashable. Ahora puedo hacer esto:
fuente
let deDuped = Set(dupes)
lo que podría devolver en un método no destructivo llamadotoSet
siempre y cuando esté de acuerdo con el cambio de tipoSi desea obtener información sobre cómo extender Arrays y otros tipos de código de pago de compilación en clases en este repositorio de Github https://github.com/ankurp/Cent
A partir de Xcode 6.1, la sintaxis para extender las matrices es la siguiente
fuente
Eché un vistazo a los encabezados de la biblioteca estándar de Swift 2, y aquí está el prototipo de la función de filtro, lo que hace que sea bastante obvio cómo rodar el suyo.
No es una extensión de Array, sino de CollectionType, por lo que el mismo método se aplica a otros tipos de colección. @noescape significa que el bloque pasado no dejará el alcance de la función de filtro, lo que permite algunas optimizaciones. El yo con S mayúscula es la clase que estamos ampliando. Self.Generator es un iterador que recorre los objetos de la colección y Self.Generator.Element es el tipo de 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 tome un elemento de la colección y devuelva un Bool, y devuelve una matriz del tipo original. Así que, juntando esto, aquí hay un método que me parece útil: combina mapa y filtro, al tomar un bloque que asigna un elemento de colección a un valor opcional, y devuelve una matriz de esos valores opcionales que no son nulos.
fuente
fuente
( Swift 2.x )
También puede extender la matriz para que se ajuste a 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 genéricos que se ajusten a alguna restricción de tipo, por ejemplo, protocolo
MyTypes
. La ventaja de usar este enfoque es que puede escribir funciones tomando argumentos de matriz genéricos, con la restricción de que estos argumentos de matriz deben ajustarse a su protocolo de utilidades de funciones personalizadas, digamos protocoloMyFunctionalUtils
.Puede obtener este comportamiento implícitamente, al restringir los elementos de la matriz a
MyTypes
, o --- como mostraré en el método que describo a continuación ---, de manera bastante clara y explícita, dejando que el encabezado genérico de las funciones de la matriz muestre directamente las matrices de entrada se ajusta aMyFunctionalUtils
.Comenzamos con Protocolos
MyTypes
para usar como restricción de tipo; amplíe los tipos que desea incluir en sus genéricos mediante este protocolo (el ejemplo a continuación amplía los tipos fundamentalesInt
yDouble
también un tipo personalizadoMyCustomType
)Protocolo
MyFunctionalUtils
(que contiene planos de nuestras utilidades de funciones de matriz genéricas adicionales) y, posteriormente, la extensión de Array porMyFunctionalUtils
; implementación de métodos impresos en azul:Finalmente, pruebas y dos ejemplos que muestran una función que toma matrices genéricas, con los siguientes casos, respectivamente
Mostrando una 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 la matriz a 'MyTypes' (función
bar1
).Mostrando explícitamente que los parámetros de la matriz se ajustan al protocolo 'MyFunctionalUtils' (función
bar2
).La prueba y los ejemplos siguientes:
fuente
fuente
$0 as! Double
) están luchando contra el sistema de tipos de Swift y, en mi opinión, también anulan el propósito de la pregunta del OP. Al hacerlo, está perdiendo cualquier potencial para las optimizaciones del compilador para los cálculos que realmente desea hacer, y también está contaminando el espacio de nombres de Array con funciones sin sentido (¿por qué querría ver .calculateMedian () en una matriz de UIViews, o de cualquier cosa menos doble para el caso?). Hay una mejor manera.extension CollectionType where Generator.Element == Double {}