Le comportement est extrêmement similaire à la méthode Array.Resize
en .NET. Pour comprendre ce qui se passe, il peut être utile de regarder l'histoire du jeton .
en C, C++, Java, C# et Swift.
En C, une structure n'est rien de plus qu'une agrégation de variables. Appliquer le .
à une variable de type structure permettra d'accéder à une variable stockée dans la structure. Les pointeurs vers des objets ne détiennent pas des agrégations de variables, mais les identifient. Si on a un pointeur qui identifie une structure, l'opérateur ->
peut être utilisé pour accéder à une variable stockée dans la structure identifiée par le pointeur.
En C++, les structures et les classes non seulement agrègent des variables, mais peuvent également leur attacher un code. Utiliser le .
pour invoquer une méthode sur une variable demandera à cette méthode d'agir sur le contenu de la variable elle-même ; utiliser ->
sur une variable qui identifie un objet demandera à cette méthode d'agir sur l'objet identifié par la variable.
En Java, tous les types de variables personnalisés identifient simplement des objets, et invoquer une méthode sur une variable permettra à la méthode de savoir quel objet est identifié par la variable. Les variables ne peuvent pas contenir de type de données composite directement, et il n'existe aucun moyen pour une méthode d'accéder à une variable sur laquelle elle est invoquée. Ces restrictions, bien que limitantes sémantiquement, simplifient grandement l'exécution, facilitent la validation du bytecode ; de telles simplifications ont réduit la surcharge de ressources de Java à un moment où le marché était sensible à de tels problèmes, et ont ainsi contribué à son gain de popularité sur le marché. Cela signifie également qu'il n'y avait pas besoin d'un jeton équivalent au .
utilisé en C ou en C++. Bien que Java aurait pu utiliser ->
de la même manière que C et C++, les créateurs ont opté pour le caractère unique .
puisqu'il n'était pas nécessaire pour un autre but.
En C# et dans d'autres langages .NET, les variables peuvent soit identifier des objets, soit contenir des types de données composites directement. Lorsqu'il est utilisé sur une variable d'un type de données composite, .
agit sur le contenu de la variable ; lorsqu'il est utilisé sur une variable de type référence, .
agit sur l'objet identifié par elle. Pour certains types d'opérations, la distinction sémantique n'est pas particulièrement importante, mais pour d'autres, elle l'est. Les situations les plus problématiques sont celles où une méthode du type de données composite qui modifierait la variable sur laquelle elle est invoquée est invoquée sur une variable en lecture seule. Si une tentative est faite pour invoquer une méthode sur une valeur ou une variable en lecture seule, les compilateurs copieront généralement la variable, laisseront la méthode agir sur celle-ci, et la jetteront. Cela est généralement sûr avec les méthodes qui ne font que lire la variable, mais pas sûr avec les méthodes qui écrivent dessus. Malheureusement, .does n'a pas encore de moyen d'indiquer quelles méthodes peuvent être utilisées en toute sécurité avec une telle substitution et lesquelles ne le peuvent pas.
En Swift, les méthodes sur les agrégats peuvent indiquer expressément si elles modifieront la variable sur laquelle elles sont invoquées, et le compilateur interdira l'utilisation de méthodes mutantes sur des variables en lecture seule (au lieu de les muter des copies temporaires de la variable qui seront ensuite jetées). En raison de cette distinction, utiliser le jeton .
pour appeler des méthodes qui modifient les variables sur lesquelles elles sont invoquées est beaucoup plus sûr en Swift qu'en .NET. Malheureusement, le fait que le même jeton .
soit utilisé à cette fin que pour agir sur un objet externe identifié par une variable signifie que la possibilité de confusion persiste.
S'il avait une machine à remonter le temps et revenait à la création de C# et/ou de Swift, on pourrait éviter rétroactivement une grande partie de la confusion entourant de tels problèmes en ayant les langages utiliser les jetons .
et ->
d'une manière beaucoup plus proche de l'utilisation en C++. Les méthodes des agrégats et des types de référence pourraient utiliser .
pour agir sur la variable sur laquelle elles ont été invoquées, et ->
pour agir sur une valeur (pour les composites) ou la chose identifiée par celle-ci (pour les types de référence). Cependant, aucun de ces langages n'est conçu de cette manière.
En C#, la pratique normale pour qu'une méthode modifie une variable sur laquelle elle est invoquée est de passer la variable en tant que paramètre ref
à une méthode. Ainsi, appeler Array.Resize(ref someArray, 23);
lorsque someArray
identifie un tableau de 20 éléments fera en sorte que someArray
identifie un nouveau tableau de 23 éléments, sans affecter le tableau original. L'utilisation de ref
indique clairement que la méthode devrait être censée modifier la variable sur laquelle elle est invoquée. Dans de nombreux cas, il est avantageux de pouvoir modifier des variables sans avoir à utiliser de méthodes statiques ; Swift aborde ce moyen en utilisant la syntaxe .
. L'inconvénient est qu'il perd la clarté quant aux méthodes qui agissent sur des variables et aux méthodes qui agissent sur des valeurs.
2 votes
Les sémantiques des tableaux Swift sont assez confuses, ils disent que c'est une valeur de type. Voici une discussion à ce sujet devforums.apple.com/message/975661#975661
95 votes
Pour mémoire, je ne pense pas que cette question devrait être fermée. Swift est un nouveau langage, il y aura donc des questions comme celle-ci pendant un moment pendant que nous apprenons tous. Je trouve cette question très intéressante et j'espère que quelqu'un aura un argument convaincant en sa faveur.
0 votes
Juste pour clarifier. La décision de conception centrale prise ici d'avoir
.append
"modifier" le tableau plutôt que (de manière plus judicieuse à mon avis) retourner une nouvelle séquence. Puisque les tableaux sont immuables (?) en swift cela provoque une copie implicite désagréable. Et c'est ce qui pose problème à l'OP, non pas aux tableaux en soi, mais qu'il y a quelque chose qui ne va pas avec l'implémentation deappend
en ayant son gâteau mutable/immuable et le mangeant également?4 votes
@Joel Fine, demandez-le aux programmeurs, Stack Overflow est réservé aux problèmes de programmation spécifiques et non opinionnés.
21 votes
@bjb568: Ce n'est pas une opinion, cependant. Cette question devrait être répondue avec des faits. Si un développeur Swift vient et répond "Nous l'avons fait comme ça pour X, Y et Z", alors c'est simplement un fait. Vous pouvez ne pas être d'accord avec X, Y et Z, mais si une décision a été prise pour X, Y et Z alors c'est simplement un fait historique de la conception du langage. Tout comme quand j'ai demandé pourquoi
std::shared_ptr
n'a pas de version non-atomique, il y avait une réponse basée sur des faits, pas une opinion (le fait est que le comité l'a considéré mais ne le voulait pas pour diverses raisons).0 votes
@Cornstalks Oui, d'accord, alors trop général.
0 votes
@Cornstalks Ce n'est pas un fait, c'est toujours une opinion. L'opinion des créateurs du langage. La question est de savoir si cette conception est mauvaise ou non, ce qui est une opinion. Cela ne signifie pas que les opinions sur ce sujet ne sont pas précieuses, mais qu'elles ne sont pas adaptées à StackOverflow.
7 votes
@JasonMArcher: Seul le tout dernier paragraphe est basé sur l'opinion (qui, oui, pourrait peut-être être supprimée). Le titre réel de la question (que je prends comme la question elle-même) est répondable avec des faits. Il y a une raison pour laquelle les tableaux ont été conçus pour fonctionner de la manière dont ils fonctionnent.
1 votes
C'est la chose la plus stupide que j'ai jamais entendue - il n'y a pas d'excuse pour cela. facepalm. Les tableaux devraient simplement être des objets appropriés. Je suppose qu'une solution de contournement serait simplement d'utiliser des tableaux Obj-C? NSArray et NSMutableArray devraient toujours fonctionner. Implémenter une stratégie de tableau "cela pourrait être une copie, mais peut-être pas" ajoute de la complication pour aucune raison.
0 votes
Je suppose qu'une solution consiste à envelopper tout tableau dans des objets appropriés lors de leur passage, plutôt que de définir directement une autre variable comme un tableau (ce qui pourrait finalement se traduire par une copie et est donc imprévisible).
0 votes
Ils ont copié la mutation de la structure mais pas la mutation du contenu ? Chapeau pour rendre tout confus. Ils devraient aller dans un sens ou dans l'autre. (Si
a[1]=42
était également copié s'il était partagé, cela serait entièrement justifiable, peut-être un peu plus difficile à rendre rapide. Il suffit de créer une vue mutable sur un modèle immuable de base.)0 votes
Je suggérerais que le dernier paragraphe soit ajusté légèrement, à "Ce comportement semble être ad hoc; existe-t-il un principe qui détermine quand le tableau est copié et quand il ne l'est pas?"
1 votes
Je me demande pourquoi
e[0..2] = [4, 5]
ne change pas la longueur??0 votes
Comment je l'explique en Java : En quelque sorte, les tableaux Swift peuvent se réaffecter eux-mêmes, comme
this = new A()
0 votes
Cela est généralement appelé "Copy-on-Write" d'ailleurs, simplement qu'il a été implémenté à moitié (a[1]=6 devrait également créer une copie)
7 votes
Oui, comme l'a dit API-Beast, cela est généralement appelé "Copie-sur-un-Design-de-langage-de-Merde".
0 votes
Java et C# n'ont pas de longueur modifiable pour les tableaux (ce qui est très positif à mon avis). Le realloc de C est similaire à la création d'un nouveau tableau et à la copie dedans, donc pratiquement la même chose qu'en Java/C#. Changer la longueur d'un tableau peut nécessiter de changer la valeur de référence, ce qui explique le comportement étrange - c'est-à-dire accéder à
e[0..2]
peut entraîner une valeur différente du pointeur vers le tableau contenu danse
. Pour simplifier, il n'y a pas de moyen de mettre en œuvre des tableaux sains avec une longueur modifiable mais un pointeur de pointeur - c'est-à-dire des tableaux enveloppés. Juste pour être sûr : la conception est vraiment stupide. C'est une explication pourquoi le résultat ressemble à ça.1 votes
@R.MartinhoFernandes COW est un concept intéressant, j'ai hâte de voir COHALD mentionné dans les livres d'introduction à Swift à partir de maintenant.
0 votes
Actuellement (probablement à partir de Swift 2, si je devais deviner) les tableaux toujours utilisent COW, sans aucun doute. Cette question ne s'applique plus car toute modification - fondamentalement, toute méthode
mutating
- sur une instance partagée d'un tableau déclenchera une copie de ce tableau.