J'étudiais sur les syndicats disjoints dans la programmation. Je suis tombé sur le dicton selon lequel Pascal
, SML
et C
ont leur propre version d'union: variant record
, < code> construction et union
. Cela disait aussi que Pascal
contient une "balise" que vous n'avez pas à utiliser, SML
a une balise dont vous avez besoin pour l'utiliser et C
n'a pas de balise. de plus, SML
lancera une exception si nous l'avons mal utilisé, Pascal
permet la vérification pendant l'exécution et C
n'a pas de fonction de vérification pendant l'exécution et le programmeur doit ajouter manuellement un champ pour une "balise".
Tout d'abord, je ne comprends pas ce qu'est "tag". J'essayais de regarder quelques exemples de ces syndicats, mais je ne comprenais pas ce que représentait «tag». Si les "balises" sont importantes, comment se fait-il que C
en ait une? quelle est la différence entre ces syndicats.
De plus, je n'ai trouvé aucun matériel lié à la "balise" des syndicats.
De plus, qu'est-ce que cela signifie «vérifier pendant l'exécution», vérifier quoi? Ce sera formidable de voir plusieurs exemples illustrant ces fonctionnalités.
4 Réponses :
La balise est tout ce qui vous indique quel membre du syndicat est actuellement utilisé. C'est généralement une énumération mais peut être un entier, un booléen ou un champ de bits basé sur l'un de ces éléments.
Exemple:
union my_union { char *string; void *void_ptr; long integer; }; struct my_tagged_union { union my_union the_union; enum { is_string, is_void_ptr, is_integer } the_tag; };
C ne vous obligeant pas à utiliser un -tag signifie que vous avez plus de contrôle sur la mise en page et la taille de vos données. Par exemple, vous pouvez utiliser une balise de champ de bits et la placer à côté d'autres informations de champ de bits que vous stockez dans votre structure afin que les champs de bits soient fusionnés, ce qui vous permet d'économiser de l'espace; ou parfois le membre du syndicat actuellement utilisé est implicite du contexte dans lequel se trouve votre code, auquel cas aucune balise n'est nécessaire.
Tout d'abord, je ne comprends pas ce qu'est "tag".
Wikipedia a une discussion assez intéressante sur le concept global , qui commence par une liste de synonymes, y compris «union taguée». En fait, "union marquée" est le titre principal de l'article, et l'union disjointe est l'un des synonymes. Cela commence par une explication assez succincte:
une structure de données utilisée pour contenir une valeur pouvant prendre plusieurs types différents, mais fixes. Un seul des types peut être utilisé à à tout moment, et un champ tag indique explicitement lequel se trouve dans utiliser.
Vous continuez à demander,
Si les "balises" sont importantes, comment se fait-il que C en ait une?
L'importance des balises dans ce contexte est une question de conception de langage sur laquelle C, Pascal et SML prennent des positions différentes. Dans la mesure où C est enclin à adopter une approche plutôt basique de la plupart des choses et à permettre aux utilisateurs un grand contrôle, il n'est pas surprenant qu'il ne force pas l'utilisation des balises. Les utilisateurs qui souhaitent des balises peuvent les implémenter eux-mêmes avec une relative facilité, comme je l'ai d'ailleurs fait moi-même à l'occasion.
Alternativement, il peut être plus facile de dire que C n'a pas de tag syndicats en tant que fonctionnalité de langage intégrée du tout, seulement des unions simples et non étiquetées. De ce point de vue, si vous voulez une union étiquetée en C, vous devez l'implémenter vous-même. C'est probablement la vue la plus cohérente, mais je suppose qu'elle diffère de celle présentée dans le matériel que vous avez étudié.
quelle est la différence entre ces syndicats.
Ce sont des implémentations différentes d'un concept similaire, fournies par différents langages. Une analyse complète serait au-delà de la portée raisonnable d'une réponse SO. Comme beaucoup de choses en informatique et ailleurs, l ' idée abstraite des unions disjointes peut être réalisée de nombreuses manières différentes.
De plus, je n'ai trouvé aucun matériel lié à la "balise" des syndicats.
Voir ci-dessus et l'article Wikipédia lié. Je suis sûr que vous pourriez également trouver beaucoup plus de matériel, en particulier avec la liste de synonymes de WP.
De plus, que signifie "vérifier pendant l'exécution", vérifier quoi?
Je devrais voir le contexte et la déclaration exacte pour être sûr, mais il semble probable que votre source parlait de vérifier une ou plusieurs de ces choses:
- que la balise d'une instance particulière de l'union est l'une de celles définies pour ce type d'union, ou
- que le contenu de l'union est du type indiqué par la balise, ou
- qu'une liste d'actions alternatives (voir ci-dessous) couvre toutes les alternatives possibles.
Ce sera formidable de voir plusieurs exemples illustrant ces fonctionnalités.
Mon Pascal est trop rouillé pour être d'une quelconque utilité, et je ne connais pas SML. Cependant, même un simple exemple en C peut être instructif:
void print_union(union disjoint_union du) { switch (du.tag) { case INT_TAG: printf("%d", du.an_int); break; case STRING_TAG: printf("%s", du.a_string); break; case DOUBLE_TAG: printf("%f", du.a_double); break; } }Ceci étant C, la balise est fournie manuellement et explicitement, et le langage ne la distingue pas spécialement. Il appartient également au programmeur de s'assurer que le contenu de l'union porte la bonne balise.
Vous pouvez utiliser une telle chose avec une fonction comme celle-ci, qui s'appuie sur la balise pour déterminer comment gérer les instances de le type d'union:
enum my_tag { INT_TAG, STRING_TAG, DOUBLE_TAG }; union disjoint_union { struct { enum my_tag tag; int an_int; }; struct { enum my_tag tag_s; char *a_string; }; struct { enum my_tag tag_d; double a_double; }; }; union disjoint_union u = { .tag = INT_TAG, .an_int = 42 }; union disjoint_union u2 = { .tag = STRING_TAG, .a_string = "hello" }; union disjoint_union u3 = { .tag = DOUBLE_TAG, .a_double = 3.14159 };
On pourrait appeler ces unions disjointes une forme très précoce de polymorphisme. Vous avez un type qui peut avoir plusieurs formes. Dans certaines langues, laquelle de ces formes est utilisée (est active) se distingue par un membre du type, appelé balise. Cela peut être un booléen, un octet, une énumération ou un autre ordinal.
Dans certaines versions (plus anciennes?) De Pascal, la balise doit en fait contenir la valeur correcte. Une "union" Pascal (ou, comme on les appelle en Pascal, enregistrement de variante ) contient une valeur qui distingue laquelle des branches est actuellement "active".
Un exemple:
// The following is more or less the equivalent of the Pascal record above struct MyVariantRec { int first; double second; union { struct { unsigned char b0, b1, b2, b3; }; struct { unsigned short w0, w1 }; struct { long l }; }; }
Dans de telles versions de Pascal, si Tag a la valeur 0, vous ne pouvez accéder qu'à B0, B1, B2 ou B3 et pas aux autres variantes. Si Tag vaut 1, vous ne pouvez accéder qu'à W0 et W1, etc ...
Dans la plupart des versions Pascal, il n'y a pas de telle restriction et la valeur de la balise est purement informative. Dans beaucoup d'entre eux, vous n'avez même plus besoin d'une valeur de balise explicite:
type MyVariantRec = record First: Integer; // the non-variant part begins here Second: Double; case Byte of // only the following part is a "union", the variant part. 0: ( B0, B1, B2, B3: Byte; ); 1: ( W0, W1: Word; ); 2: ( L: Longint); end;
Notez que les enregistrements de variantes Pascal ne sont pas de pures unions, où chaque partie est une alternative:
MyUnion = record case Byte of // no tag, just a type, to keep the syntax similar etc...
En C, vous devrez imbriquer une union dans une structure pour obtenir quelque chose à peu près identique:
type MyUnion = record // Pascal's version of a struct -- or union case Tag: Byte of // This doesn't have to be called Tag, it can have any name 0: (B0, B1, B2, B3: Byte); // only one of these branches is present 1: (W0, W1: Word); // they overlap each other in memory 2: (L: Longint); end;
SML a une balise dont vous avez besoin pour l'utiliser [...] . de plus, SML lèvera une exception si nous l'avons mal utilisée,
Le ML standard contient des types de données algébriques qui contiennent des types de somme et types de produits . Les types de somme reposent sur des unions (et les types de produits sur des structures), mais gèrent automatiquement ce que vous appelez une union balisée ou disjointe dans le compilateur; vous spécifiez les constructeurs , et le code compilé détermine comment différencier les différents constructeurs via la correspondance de modèles . Par exemple,
datatype pokemon = Pikachu of int | Bulbasaur of string | Charmander of bool * char | Squirtle of pokemon listAinsi, un type somme peut avoir différents constructeurs avec des paramètres différents, et les paramètres peuvent eux-mêmes être un produit d'autres types, y compris somme des types, et incluant le type défini lui-même, ce qui rend la définition de type de données récursive. Ceci est implémenté avec des unions balisées, mais les abstractions en haut fournissent plus de commodité syntaxique.
Pour clarifier, Standard ML ne lèvera pas d'exception s'il est mal utilisé, mais renvoie une erreur de type em > lors de la compilation. Cela est dû au système de type de ML standard . Vous ne pouvez donc pas accidentellement avoir un pointeur
(void *)
que vous lancez vers quelque chose qui n'est pas, ce qui est possible en C.
Il s'agit probablement de tenir des registres pour savoir quel type de membre le syndicat stocke.