Intéressant

Comment faire des copies profondes en Ruby

Comment faire des copies profondes en Ruby

Il est souvent nécessaire de copier une valeur en Ruby. Bien que cela puisse sembler simple, et que ce soit pour des objets simples, dès que vous devez copier une structure de données avec plusieurs tableaux ou hachages sur le même objet, vous constaterez rapidement qu'il existe de nombreux pièges.

Objets et références

Pour comprendre ce qui se passe, examinons un code simple. Tout d’abord, l’opérateur d’affectation utilisant un type POD (Plain Old Data) en Ruby.

a = 1
b = a
a + = 1
met b

Ici, l’opérateur d’affectation fait une copie de la valeur de une et l'assigner à b en utilisant l'opérateur d'affectation. Tout changement à une ne sera pas reflété dans b. Mais qu'en est-il de quelque chose de plus complexe? Considère ceci.

a = 1,2
b = a
a << 3
met b.inspect

Avant d’exécuter le programme ci-dessus, essayez de deviner quelle sera la sortie et pourquoi. Ce n'est pas la même chose que dans l'exemple précédent, les modifications apportées à une se reflètent dans b, mais pourquoi? En effet, l'objet Array n'est pas un type POD. L’opérateur d’affectation ne copie pas la valeur, il copie simplement la valeur. référence à l'objet Array. le une et b les variables sont maintenant les références Dans le même objet Array, les modifications de l'une ou l'autre variable seront visibles dans l'autre.

Et vous pouvez maintenant voir pourquoi la copie d'objets non triviaux avec des références à d'autres objets peut être délicate. Si vous faites simplement une copie de l'objet, vous copiez simplement les références aux objets plus profonds, votre copie est donc appelée "copie superficielle".

Ce que Ruby fournit: dup et clone

Ruby fournit deux méthodes pour faire des copies d'objets, dont une qui peut être faite pour faire des copies profondes. le Objet # dup La méthode effectuera une copie superficielle d’un objet. Pour y parvenir, le dup la méthode appellera le initialiser_copie méthode de cette classe. Ce que cela fait exactement dépend de la classe. Dans certaines classes, telles que Array, un nouveau tableau sera initialisé avec les mêmes membres que le tableau d'origine. Ceci, cependant, n'est pas une copie profonde. Considérer ce qui suit.

a = 1,2
b = a.dup
a << 3
met b.inspect
a = 1,2
b = a.dup
a0 << 3
met b.inspect

Que s'est-il passé ici? le Tableau # initialize_copy méthode va effectivement faire une copie d’un tableau, mais cette copie est elle-même une copie superficielle. Si vous avez d’autres types de non-POD dans votre tableau, utilisez dup sera seulement une copie partiellement profonde. Il sera seulement aussi profond que le premier tableau, les tableaux plus profonds, les hachages ou autres objets ne seront copiés que de manière superficielle.

Il y a une autre méthode qui mérite d'être mentionnée, cloner. La méthode clone fait la même chose que dup avec une distinction importante: il est prévu que les objets substituent cette méthode à une autre capable de faire des copies complètes.

Donc, en pratique, qu'est-ce que cela signifie? Cela signifie que chacune de vos classes peut définir une méthode de clonage qui effectuera une copie complète de cet objet. Cela signifie également que vous devez écrire une méthode de clonage pour chaque classe que vous créez.

Un truc: Marshalling

"Marquer" un objet est une autre façon de dire "sérialiser" un objet. En d'autres termes, transformez cet objet en un flux de caractères pouvant être écrit dans un fichier que vous pouvez "dissocier" ou "désérialiser" ultérieurement pour obtenir le même objet. Ceci peut être exploité pour obtenir une copie détaillée de tout objet.

a = 1,2
b = Marshal.load (Marshal.dump (a))
a0 << 3
met b.inspect

Que s'est-il passé ici? Marshal.dump crée un "dump" du tableau imbriqué stocké dans une. Ce vidage est une chaîne de caractères binaires destinée à être stockée dans un fichier. Il abrite le contenu complet du tableau, une copie complète complète. Prochain, Marshal.load fait le contraire. Il analyse ce tableau de caractères binaires et crée un tableau complètement nouveau, avec de nouveaux éléments de tableau.

Mais c'est un truc. C'est inefficace, cela ne fonctionnera pas sur tous les objets (que se passera-t-il si vous essayez de cloner une connexion réseau de cette manière?) Et ce n'est probablement pas très rapide. Cependant, c’est le moyen le plus simple de créer des copies complètes sans utiliser de personnalisation. initialiser_copie ou cloner méthodes. En outre, la même chose peut être faite avec des méthodes comme to_yaml ou to_xml si vous avez des bibliothèques chargées de les prendre en charge.