Comment puis-je étendre Swift Array<T>
ou T[]
taper avec des utilitaires fonctionnels personnalisés?
La navigation dans les documents de l'API de Swift montre que les méthodes Array sont une extension de T[]
, par exemple:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
Lorsque vous copiez et collez la même source et essayez des variantes telles que:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
Il ne parvient pas à générer avec l'erreur:
Le type nominal
T[]
ne peut pas être étendu
L'utilisation de la définition de type complète échoue avec Use of undefined type 'T'
, c'est-à-dire:
extension Array<T> {
func foo(){}
}
Et cela échoue également avec Array<T : Any>
et Array<String>
.
Curieusement Swift me permet d'étendre un tableau non typé avec:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Avec quoi il me permet d'appeler:
[1,2,3].each(println)
Mais je ne peux pas créer une extension de type générique appropriée car le type semble être perdu lorsqu'il passe par la méthode, par exemple en essayant de remplacer le filtre intégré de Swift par :
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
}
}
Mais le compilateur le traite comme non typé où il permet toujours d'appeler l'extension avec:
["A","B","C"].find { $0 > "A" }
Et lorsqu'il est exécuté avec un débogueur indique que le type est, Swift.String
mais c'est une erreur de construction pour essayer d'y accéder comme une chaîne sans le transtyper en String
premier, c'est-à-dire:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Quelqu'un sait-il quelle est la bonne façon de créer une méthode d'extension typée qui agit comme les extensions intégrées?
extension T[]
bit lorsque vous cliquez sur le type de tableau dans XCode, mais ne voyez aucun moyen de l'implémenter sans obtenir d'erreur.<T>
de la signature de la méthode.Réponses:
Pour étendre des tableaux typés avec des classes , ce qui suit fonctionne pour moi (Swift 2.2 ). Par exemple, trier un tableau typé:
Essayer de le faire avec une structure ou des typealias donnera une erreur:
Mise à jour :
Pour étendre des tableaux typés avec des non-classes, utilisez l'approche suivante:
Dans Swift 3, certains types ont été renommés:
la source
[Iterator.Element]
?Après avoir essayé différentes choses, la solution semble supprimer le
<T>
de la signature comme:Qui fonctionne maintenant comme prévu sans erreurs de construction:
la source
filter
fonction existante :let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
est fonctionnellement équivalent au vôtrefind
, c'est-à-dire que le résultat de la fonction est le même. Si votre fermeture de filtre a des effets secondaires, vous pourriez ne pas aimer les résultats, c'est certain.filter
.filter
,map
et lesreduce
fonctions proviennent, les fonctions sont exécutées pour leurs valeurs de retour. En revanche, laeach
fonction que vous définissez ci-dessus est un exemple de fonction exécutée pour son effet secondaire, car elle ne renvoie rien. Je suppose que nous pouvons convenir que l'implémentation actuelle de Swift n'est pas idéale et que la documentation ne dit rien sur ses caractéristiques d'exécution.Étendez tous les types:
Étendez certains types:
Étendez un type particulier :
la source
J'ai eu un problème similaire - je voulais étendre le tableau général avec une méthode swap (), qui était censée prendre un argument du même type que le tableau. Mais comment spécifiez-vous le type générique? J'ai trouvé par essais et erreurs que ce qui suit fonctionnait:
La clé était le mot «élément». Notez que je n'ai défini ce type nulle part, il semble exister automatiquement dans le contexte de l'extension du tableau et faire référence à quel que soit le type des éléments du tableau.
Je ne suis pas sûr à 100% de ce qui se passe là-bas, mais je pense que c'est probablement parce que «Element» est un type associé du tableau (voir «Types associés» ici https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )
Cependant, je ne vois aucune référence à cela dans la référence de structure Array ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) ... donc je suis encore un peu incertain.
la source
Array
est un type générique:Array<Element>
(voir swiftdoc.org/v2.1/type/Array ),Element
est un espace réservé pour le type contenu. Par exemple:var myArray = [Foo]()
signifie quemyArray
ne contiendra que du typeFoo
.Foo
dans ce cas est "mappé" à l'espace réservé génériqueElement
. Si vous souhaitez modifier le comportement général d'Array (via l'extension), vous utiliserez l'espace réservé génériqueElement
et non aucun type concret (comme Foo).Utilisation de Swift 2.2 : J'ai rencontré un problème similaire en essayant de supprimer les doublons d'un tableau de chaînes. J'ai pu ajouter une extension sur la classe Array qui fait exactement ce que je cherchais à faire.
L'ajout de ces deux méthodes à la classe Array me permet d'appeler l'une des deux méthodes sur un tableau et de supprimer correctement les doublons. Notez que les éléments du tableau doivent être conformes au protocole Hashable. Maintenant, je peux le faire:
la source
let deDuped = Set(dupes)
, que vous pourriez retourner dans une méthode non destructive appeléetoSet
tant que vous êtes d'accord avec le changement de typeSi vous souhaitez en savoir plus sur l'extension des tableaux et d'autres types de code de contrôle de construction dans les classes dans ce dépôt github https://github.com/ankurp/Cent
Depuis Xcode 6.1, la syntaxe pour étendre les tableaux est la suivante
la source
J'ai jeté un coup d'œil aux en-têtes de bibliothèque standard de Swift 2, et voici le prototype de la fonction de filtrage, ce qui rend très évident comment rouler le vôtre.
Ce n'est pas une extension d'Array, mais de CollectionType, donc la même méthode s'applique aux autres types de collection. @noescape signifie que le bloc transmis ne quittera pas la portée de la fonction de filtrage, ce qui permet certaines optimisations. Le moi avec un S majuscule est la classe que nous étendons. Self.Generator est un itérateur qui parcourt les objets de la collection et Self.Generator.Element est le type des objets, par exemple pour un tableau [Int?] Self.Generator.Element serait Int ?.
Dans l'ensemble, cette méthode de filtrage peut être appliquée à n'importe quel CollectionType, elle a besoin d'un bloc de filtre qui prend un élément de la collection et renvoie un Bool, et elle retourne un tableau du type d'origine. Donc, pour mettre cela ensemble, voici une méthode que je trouve utile: il combine la carte et le filtre, en prenant un bloc qui mappe un élément de collection à une valeur facultative, et renvoie un tableau de ces valeurs facultatives qui ne sont pas nulles.
la source
la source
( Swift 2.x )
Vous pouvez également étendre le tableau pour qu'il soit conforme à un protocole contenant des plans directeurs pour les méthodes de type générique, par exemple, un protocole contenant vos utilitaires fonctionnels personnalisés pour tous les éléments de tableau génériques conformes à une contrainte de type, par exemple le protocole
MyTypes
. L'avantage de cette approche est que vous pouvez écrire des fonctions en prenant des arguments de tableau génériques, avec une contrainte que ces arguments de tableau doivent être conformes à votre protocole d'utilitaires de fonctions personnalisés, par exemple le protocoleMyFunctionalUtils
.Vous pouvez obtenir ce comportement soit implicitement, en saisissant les éléments du tableau sous contrainte
MyTypes
, soit --- comme je vais le montrer dans la méthode que je décris ci-dessous ---, de manière assez claire et explicite, en laissant votre en-tête de fonctions de tableau générique montrer directement que les tableaux d'entrée conforme àMyFunctionalUtils
.Nous commençons par les protocoles
MyTypes
à utiliser comme contrainte de type; étendre les types que vous souhaitez adapter à vos génériques par ce protocole (l'exemple ci-dessous étend les types fondamentauxInt
etDouble
ainsi qu'un type personnaliséMyCustomType
)Protocole
MyFunctionalUtils
(contenant les plans de nos utilitaires de fonctions de tableau génériques supplémentaires) et par la suite, l'extension de Array parMyFunctionalUtils
; mise en œuvre de méthodes imprimées en bleu:Enfin, des tests et deux exemples montrant une fonction prenant des tableaux génériques, avec respectivement les cas suivants
Montrant l' assertion implicite que les paramètres du tableau sont conformes au protocole «MyFunctionalUtils», via le type contraignant les éléments du tableau à «MyTypes» (fonction
bar1
).Montrant explicitement que les paramètres du tableau sont conformes au protocole 'MyFunctionalUtils' (fonction
bar2
).Le test et les exemples suivants:
la source
la source
$0 as! Double
) luttent contre le système de type de Swift et vont également à l'encontre du but de la question du PO, à mon avis. En faisant cela, vous perdez tout potentiel d'optimisation du compilateur pour les calculs que vous voulez réellement faire, et vous polluez également l'espace de noms d'Array avec des fonctions sans signification (pourquoi voudriez-vous voir .calculateMedian () dans un tableau d'UIViews, ou d'autre chose que Double d'ailleurs?). Il y a un meilleur moyen.extension CollectionType where Generator.Element == Double {}