Coffre-fort (limites-vérifié) tableau de recherche de Swift, par l'intermédiaire de liaisons facultatives?
Si j'ai un tableau dans Swift, et essayez d'accéder à un index qui est en dehors des limites, il n'est pas surprenant d'erreur à l'exécution:
var str = ["Apple", "Banana", "Coconut"]
str[0] //"Apple"
str[3] // EXC_BAD_INSTRUCTION
Cependant, j'aurais pensé avec toutes les option de chaînage et sécurité que Swift apporte, il serait trivial de faire quelque chose comme:
let theIndex = 3
if let nonexistent = str[theIndex] { //Bounds check + Lookup
print(nonexistent)
...do other things with nonexistent...
}
Au lieu de:
let theIndex = 3
if (theIndex < str.count) { //Bounds check
let nonexistent = str[theIndex] //Lookup
print(nonexistent)
...do other things with nonexistent...
}
Mais ce n'est pas le cas - je utiliser les ol' if
déclaration de vérifier et de garantir l'indice est inférieur à str.count
.
J'ai essayé d'ajouter mon propre subscript()
mise en œuvre, mais je ne suis pas sûr de la façon de passer l'appel à la mise en place initiale, ou pour accéder à des éléments (indice de base) sans l'aide de l'indice de notation:
extension Array {
subscript(var index: Int) -> AnyObject? {
if index >= self.count {
NSLog("Womp!")
return nil
}
return ... //What?
}
}
- Je réalise que j'ai un peu OT, mais je crois aussi qu'il serait bien, si Swift avait de la syntaxe pour effectuer toute sorte de vérification de limites, y compris les listes. Nous avons déjà un mot-clé approprié pour ce faire, dans. Ainsi, par exemple, si X (1,2,7)... ou si X dans myArray
Vous devez vous connecter pour publier un commentaire.
Alex réponse a de bons conseils et une solution pour la question, mais, je l'ai est arrivé de tomber sur une jolie manière de la mise en œuvre de cette fonctionnalité:
Swift 3.2 et versions plus récentes
Swift 3.0 et 3.1
De crédit à Hamish pour venir avec la solution pour Swift 3.
Swift 2
Exemple
safe:
nom de paramètre pour assurer la différence.return self.indices ~= index ? self[index] : nil;
-> T?
avec-> Element?
return count <= index ? nil : self[index]
également de travailler à la place dereturn indices ~= index ? self[index] : nil
. Est-il une raison de préférer l'un sur l'autre? La première est plus lisible pour moi, depuis que je suis familier avec~=
et CollectionType de la propriétéindices
.safe
.Collection
types où laIndices
ne sont pas contigus. E. g. pourSet
cas, si nous avons accès à un élément par index (SetIndex<Element>
), nous pouvons avoir des exceptions d'exécution pour les indices qui sont>= startIndex
et< endIndex
, auquel cas le coffre-fort indice échoue (voir par exemple cet exemple artificiel).O(1)
aléatoire d'accès index (que nous n'avons, cependant, également pour lacontains
Swift 2 version ci-dessus), qui ne devrait pas être un problème, cependant, à moins de travailler dans certaines HPC application avec d'énormes tableaux. Pour une autre solution (mêmeO(n)
), ce qui correspondrait à un plus direct Swift 2->3 traduction de la Swift 2 version ci-dessus, voir la réponse dans ce Q&A (utilisation decontains
permet également de "court-circuit", comme dans votre explicitewhile
solution de boucle).contains()
; il essaie d'utiliser lacontains(where: _)
. Je suis en fouillant dans les docs de référence pour suivre l' (hérité) associé types, et je ne suis pas sûr pourquoi, la condition de contrainte permet de résoudre le problème. Comment avez-vous dériver?safe
être appeléunsafe
à la place ? Je comprends que l'indice de la méthode elle-même est sûr, mais comme nous sommes en nommant le paramètre, qui est potentiellement dangereux, il me semble bizarre.safe:
contains
méthode va itérer sur tous les indices ainsi, ce qui en fait un O(n). Une meilleure façon est d'utiliser l'index et le comte de vérifier les limites.return index >= startIndex && index < endIndex ? self[index] : nil
Collection
types ontstartIndex
,endIndex
qui sontComparable
. Bien sûr, cela ne fonctionne pas pour certaines collections étranges qui, par exemple, n'ont pas d'indices dans le milieu, la solution avecindices
est le plus général.Si vous voulez vraiment ce comportement, il sent comme vous voulez un Dictionnaire au lieu d'un Tableau. Les dictionnaires de retour
nil
lors de l'accès aux touches manquantes, ce qui est logique car il est beaucoup plus difficile de savoir si une clé est présente dans un dictionnaire, puisque ces touches peut être n'importe quoi, où, dans un tableau, la clé doit dans une gamme de:0
àcount
. Et il est incroyablement commun pour itérer sur cette plage, où vous pouvez être absolument sûr ont une valeur réelle à chaque itération d'une boucle.Je pense que la raison pour laquelle il ne fonctionne pas de cette manière est un choix de conception réalisés par la Swift développeurs. Prenez votre exemple:
Si vous connaissez déjà l'indice existe, comme vous le faites dans la plupart des cas où vous utilisez un tableau, ce code est grand. Toutefois, si l'accès à un indice pourrait éventuellement revenir
nil
alors vous ont changé le type de retour deArray
'ssubscript
méthode pour être une option. Cela change votre code:Ce qui signifie que vous auriez besoin de déballer une option à chaque fois que vous itérer un tableau, ou n'importe quoi d'autre avec un indice connu, juste parce que rarement vous pouvez accéder à un en dehors des limites de l'indice. La Swift concepteurs ont opté pour moins de déballer des options, au détriment d'une exception d'exécution lors de l'accès en dehors des limites d'index. Et un accident est préférable à une logique de l'erreur provoquée par une
nil
vous ne vous attendiez pas à vos données quelque part.Et je suis d'accord avec eux. Vous ne serez donc pas de changer la valeur par défaut
Array
mise en œuvre parce que vous casserait tout le code qui s'attend à un non-valeurs facultatives à partir de baies.Au lieu de cela, vous pourriez sous-classe
Array
, et remplacersubscript
de retour facultatif. Ou, plus pratiquement, vous pouvez étendreArray
avec un non-indice méthode qui fait cela.Swift 3 Mise À Jour
func get(index: Int) ->
T?
doit être remplacé parfunc get(index: Int) ->
Element?
subscript()
à un choix - c'est le principal obstacle face de remplacer le comportement par défaut. (Je n'ai pas pu le faire fonctionner à tous.), j'ai été en évitant l'écriture d'unget()
méthode d'extension, qui est le choix évident dans d'autres scénarios (Obj-C catégories, anyone?) maisget(
n'est pas beaucoup plus grand que[
, et il est clair que le comportement peut différer de ce que les autres développeurs peuvent s'attendre à de la Swift indice de l'opérateur. Merci!!!!T
a été renomméElement
. Juste un rappel amical 🙂nil
au lieu de provoquer une exception à partir d'un hors-limites des index d'être ambigu. Depuis par exempleArray<String?>
pourrait aussi retourner nil en tant que membre de la collection, vous ne seriez pas en mesure de distinguer entre ces deux cas. Si vous avez votre propre type de collection que vous savez ne pourra jamais retourner unnil
valeur, en l'occurence, c'est contextuel de l'application, alors vous pouvez étendre Swift pour la sécurité de la vérification des limites comme répondu dans ce post.Valable dans Swift 2
Même si cela a été répondu de nombreuses fois déjà, j'aimerai vous présenter une réponse en ligne où le mode de Swift programmation est en cours, qui en Croustillant de words1 est: "Pensez
protocol
premier"• Que voulons-nous faire?
- Obtenir un Élément d'un
Array
un Index uniquement lorsque c'est sûr, etnil
sinon• Que dois-cette fonctionnalité de base, c'est une mise en œuvre sur?
-
Array
subscript
ing• Où est-il obtenir cette fonctionnalité à partir de?
- Sa définition de
struct Array
dans leSwift
module a• Rien de plus générique et abstraite?
- Il adopte
protocol CollectionType
qui assure aussi bien• Rien de plus générique et abstraite?
- Il adopte
protocol Indexable
ainsi...• Ouais, on dirait que le mieux que nous puissions faire. Peut-on alors de l'étendre à cette fonctionnalité que nous voulons?
- Mais nous avons très peu de types (pas de
Int
) et les propriétés (pas decount
) à travailler avec maintenant!• Il sera suffisant. Swift stdlib est assez bien fait 😉
1: pas vrai, mais ça donne l'idée
Collection
probablement 🙂SafeIndex
est une classe, et non pas une structure?De construire sur Nikita Kukushkin réponse, parfois, vous devez en toute sécurité attribuer à l'index de tableau ainsi que lire, c'est à dire
Voici donc une mise à jour de Nikita réponse (Swift 3.2) qui permet aussi à l'écriture à mutables les indices de tableau, en ajoutant le coffre-fort: nom du paramètre.
Swift 4
Une extension pour ceux qui préfèrent une approche plus traditionnelle de la syntaxe:
Voici quelques tests, j'ai couru pour vous:
J'ai trouvé fort array get, set, insérer, supprimer très utile. Je préfère journal et ignorer les erreurs comme tout le reste devient vite difficile à gérer. Code complet de soufflet
Tests
J'ai rembourré le tableau avec
nil
s dans mon cas d'utilisation:Vérifiez également l'indice d'extension avec
safe:
étiquette par Erica Sadun /Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/Ci-Dessus en utilisant la mention de l'extension de retour nul, à tout moment de l'indice va de lié.
résultat néant
Je pense que ce n'est pas une bonne idée. Il semble préférable de construire de solides code qui ne résulte pas en essayant de l'appliquer hors-limites des indices.
Veuillez considérer que le fait d'avoir une erreur silencieuse (comme suggéré par votre code ci-dessus) en retournant
nil
est sujette à produire encore plus complexe, plus difficile à résoudre des erreurs.Vous pourriez faire votre remplacement d'une manière similaire que vous avez utilisé et il suffit d'écrire les indices dans votre propre chemin. Seul inconvénient, c'est que le code existant ne sera pas compatible. Je pense que pour trouver un crochet pour remplacer le générique de x[i] (aussi sans un texte de préprocesseur comme en C) sera difficile.
Le plus proche que je peux penser à est de
MODIFIER: Cela fonctionne réellement. One-liner!!
if let
dans ce cas ne pas rendre le programme plus complexe, ni des erreurs plus difficile à résoudre. Simplement, il se condense deux-déclarationif
de vérification de limites réelles et de recherche en une seule bordée d', l'état condensé. Il y a des cas (en particulier travailler dans une interface utilisateur (IU) où il est normal pour un index d'être en dehors des limites, comme demander à unNSTableView
pour laselectedRow
sans sélection.countElements
ou que l'OP a fait aveccount
, tout simplement pas dans la façon dont le langage définit l'écriture tableau des indices.if theIndex < str.count && let existent = str[theIndex] {...}
?J'ai fait une simple extension pour le tableau
il fonctionne parfaitement conçu
Exemple