¿Cómo puedo extender matrices escritas en Swift?

Resuelto mythz asked hace 10 años • 12 respuestas

¿Cómo puedo extender Swift Array<T>oT[] escribir con utilidades funcionales personalizadas?

Navegar por los documentos API de Swift muestra que los métodos 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 y probar variaciones como:

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

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

No se puede compilar con el error:

El tipo nominal T[]no se puede ampliar

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

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

Y también falla con Array<T : Any>yArray<String> .

Curiosamente Swift me permite extender una matriz sin tipo con:

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

Con el que me deja llamar:

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

Pero no puedo crear una extensión de tipo genérica adecuada ya que el tipo parece perderse cuando fluye a través del método, por ejemplo, al intentar reemplazar el filtro integrado 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 y todavía permite llamar a la extensión con:

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

Y cuando se avanza con un depurador, se indica que el tipo es Swift.String, pero es un error de compilación intentar acceder a él como una cadena sin convertirlo Stringprimero, 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 escrito que actúe como las extensiones integradas?

mythz avatar Jun 04 '14 07:06 mythz
Aceptado

Para ampliar matrices escritas con clases , lo siguiente me funciona (Swift 2.2 ). Por ejemplo, ordenar una matriz escrita:

class HighScoreEntry {
    let score:Int
}

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

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

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

Actualizar :

Para ampliar matrices escritas con clases que no sean clases, utilice el siguiente enfoque:

typealias HighScoreEntry = (Int)

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

En Swift 3 se ha cambiado el nombre de algunos tipos:

extension Sequence where Iterator.Element == HighScoreEntry 
{
    // ...
}
Andrew Schreiber avatar Nov 06 '2015 00:11 Andrew Schreiber

Después de un tiempo probando cosas diferentes, la solución parece eliminar la <T>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 según lo previsto sin errores de compilación:

["A","B","C"].find { $0.compare("A") > 0 }
mythz avatar Jun 04 '2014 02:06 mythz

Ampliar todos los tipos:

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

Ampliar tipos comparables :

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

Ampliar algunos tipos:

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

Extender un tipo particular :

extension Array where Element == Int {
    // ...
}
Dmitry avatar May 11 '2019 19:05 Dmitry

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ó:

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

La clave era 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á pasando allí, pero creo que probablemente se deba a que 'Elemento' es un tipo asociado de 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 de matriz ( 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.

Daniel Howard avatar Nov 14 '2015 20:11 Daniel Howard