Accès aux membres d'une struct par nom et par indice
Céroce
Membre, Modérateur
Bonjour à tous,
J'ai une question pour ceux qui maà®trisent Swift mieux que moi.
J'ai un vecteur à trois composantes, x, y et z, et j'aimerais y accéder soit par le nom des composantes (vecteur.y), soit par un indice (vecteur[1]).
J'ai cette manière de faire, en auriez-vous une meilleure à me proposer ? En l'état, ce qui me gène est la duplication de la donnée. Mais ce qui me plaà®t est que l'accès est aussi rapide dans les deux cas.
J'ai une question pour ceux qui maà®trisent Swift mieux que moi.
J'ai un vecteur à trois composantes, x, y et z, et j'aimerais y accéder soit par le nom des composantes (vecteur.y), soit par un indice (vecteur[1]).
J'ai cette manière de faire, en auriez-vous une meilleure à me proposer ? En l'état, ce qui me gène est la duplication de la donnée. Mais ce qui me plaà®t est que l'accès est aussi rapide dans les deux cas.
struct Vec3 {
let x: Float
let y: Float
let z: Float
let components: [Float]
init(_ x: Float, _ y:Float, _ z:Float) {
self.x = x
self.y = y
self.z = z
self.components = [x, y, z]
}
}
Mots clés:
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Tu rajoutes le subscript :
Tout simplement
Moi j'ai ça :
Utilisation :
J'avoue avoir une préférence pour la version de Pyroh, la notation en subscript (vec[0]) étant moins lourde.
Oui, j'ai pensé la même chose en lisant sa solution. Son approche est plus esthétique que la mienne.
Y'a juste un truc qui me gène dans ma solution c'est le :
aÌ€ la fin du subscript. Il serait bon soit d'avoir une solution un peu plus propre ou une possibiliteÌ dans Swift de pouvoir marquer une fonction comme eÌtend faillible sans forceÌment utiliser les erreur avec un throws qui impliquerai plus de code chiant à l'usage.
En fait j'aimerai savoir si quelqu'un à une solution avant de proposer quelque chose dans Swift Evolution.
Je n'ai encore eu le temps de regarder si c'eÌtait dedans.
ça ne me pose pas de problème, sachant que c'est un cas qui n'est pas sensé arriver sauf si je fais une erreur.
À noter que je n'ai pas eu à écrire get { } dans mon implémentation.
Je suis bien consicient que la demande imposait de pouvoir accéder aux composants via un index de 0 à 2.
Dans le cas de Pyroh on ne peut pas faire :
Si il me fallait absolument cette propriété pour X raison. Le mieux serait quoi ? De passer par une propriété calculée ?
Voilà une solution qui conviendrait normalement à tout le monde :
components est certes calculeÌe mais c'est pas super grave vu qu'on a que 3 occurrences.
ApreÌ€s c'est surtout moi qui ne veut pas mettre d'init quand c'est pas vraiment neÌcessaire.
Petite question au passage. Dans ce cas là , components serait calculé à l'instanciation de l'objet ? Si oui (en admettant que Vec3 soit une classe) que se passerait il pour ce champ si je fais :
components est recalculé lorsque l'un des trois champs change de valeur ?
Je dirais que components est calculé à chaque appel.
Je dirai surtout qu'avec des let partout le compilo s'allumerai comme un sapin de Noël...
Sinon oui components est calculeÌ à chaque appel.
Je suis du même avis. D'ailleurs c'est facile à vérifier, avec un point d'arrêt ou un print().
EDIT : Preuve par l'expérience
La nouvelle version Vec3, avec l'init, et un message à chaque régénération du tableau :
Mise en oeuvre :
Résultat :
Bien vu !
Nous supposerons que nous avons des var...
J'aurais dit la même chose dans la mesure où le mot clé lazy (lazy var x: Int = {...}) n'aurait pas grand intérêt...
@Jérémy : qu'est-ce que ça change de mettre lazy ? C'est pour des let ?
Non non non
x sera calculé lorsque l'objet sera instancié alors que y sera calculé lors de son premier appel.
Exemple (le code est complètement débile mais c'est pour illustrer ce que je viens de dire) :
Jérémy. Je ne sais pas ce que tu crois que tu fasses ::)
On ne ni add, ni multiply, les coordonnées d'une pointe. En plus, là , il vaut mieux d'utiliser une struct qu'une classe
À part du fait qu'il y a déjà les structs Point, Size, etc en Swift, voici un exemple de comment on peut faire telle struct :
Ou, pour jouer plus avec le vecteur de Céroce :
8--)
@Jérémy : merci pour tes explications :-)
@Joana : merci aussi ;-)
C'est pour ça que j'ai dit :
::) ::) ::) (je suis pardonné maintenant ? :P )
De rien
Oui, mais tu retournes des Float?. Il n'y a pas de perte de performance par rapport à des Float classique, même minime ? L'utilisation de Céroce est assez .. intense, vu qu'il s'agit d'un immense flux de données graphiques.
C'est comme beaucoup de subscripts qui renvoie les optionals en cas d'échec comme index hors limites, à la place de throw
Oui mais là non...
Si on prend ce cas précis on est assuré qu'à chaque fois qu'on va utiliser une instance de Vector on aura les composantes x, y, z. C'est comme ça c'est un vecteur 3D.
Si on ajoute des subscript on sera également assurés que les indices 0, 1, 2 correspondrons respectivement au trois composantes sus-nommées. Pareil pour les clés "x", "y", "z". En dehors de ces indices et clés il n'y a aucune chance qu'aucun autre ne soit valide.
Dans ce cas là c'est fatalError sans se poser de question, pareil que pour un index out of bounds sur un Array. Si le codeur essaie de récupérer une quatrième composante c'est qu'il n'a pas compris un truc.
Faire retourner un Float? dans ce cas reviendrai à dire : "Je sais pas, essaie, on verra bien ce qu'on a !" alors que pas du tout ! Il n'y a aucune ambiguà¯té à aucun moment.
Les optionals sont là pour assainir le concept de nullité dans les cas qu'on ne peut pas prévoir avant qu'ils ne surviennent. Ici on peut tout prévoir, la clé "w" ne renverra jamais rien, ni au moment de l'écriture du code, ni lors du runtime, jamais. Pareil pour l'indice 6. Alors pas de throw, pas d'optional.
En plus ça va plomber les performances sans raison valable. L'optional binding c'est pas gratos non plus, ça peut être négligeable sur une dizaine de vecteurs mais au delà on fait clairement de la contre-optimisation et ça craint.
ApreÌ€s il y a le cas Array vs Dictionary. Array renvoie un objet de type Element tandis que Dictionary[key] renvoie un Element?. La raison est simple on peut veÌrifier le nombre d'éléments contenus dans un Array avec count. On est alors assureÌ que les indices 0..<count sont valides et reverrons quelque choses parce qu'on sait que l'indice de deÌbut est toujours 0 par convention.
Dans le cadre du Dictionary on peut aussi savoir combien il y a d'eÌleÌments dans la collection mais cette information n'est pas deÌterminante. Comme les cleÌs ne sont pas comprise dans un range il n'y a aucun moyen de les deÌduire sans les connaitre. De plus l'intérêt du Dictionary est de pouvoir avoir une collection dynamique dans laquelle l'absence d'un eÌleÌment est une information en soit. Ici les optionals font alors sens.
Les optionals sont une très bonne chose et sont très pratiques (je vous renvoie au blog d'Ali pour ça) mais il faut vraiment savoir pourquoi et quand les utiliser sinon ça devient vite contre-intuitif.
Je suis d'accord que les optionals ne sont pas appropriés ici. Dans le principe on pourrait lever une exception, mais là encore, ça obligerait à utiliser des blocs de try {}. Utiliser une assertion me parait le plus adéquat, d'autant plus que les assertions sont désactivées en Release.
f
Bah non, si tu es certain de l'existence de la donnée, tu peux forcer sa conversion.
r
A rien dans ce cas précis. C'est inutile et contre-productif d'utiliser des optionals sur des données dont l'existence est certaine.
Je pense que Joanna a utilisé un Float? pour simplifier l'écriture du code et éviter le gag du "return Float()" de Pyroh, qui ne sert qu'à éviter un hurlement de rage d'Xcode.
Si, comme Draken on ne veut pas tuer les poneys, on peut toujours utiliser un enum pour l'index :
Donc, on est garanti de ne pas excéder les limites.
Je vois pas trop l'intérêt de faire ça :
si on peut faire ça :
Car à la place des Ints pour les indexes, on aurait un type intégral qui garanti de ne pas dépasser les limites
Extrait de mon code:
Sans l'accès aux composantes par indice, on doit répéter 3 fois le même code.