Comment faire des copies profondes dans Ruby

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

Objets et références

Pour comprendre ce qui se passe, regardons un code simple. Tout d'abord, l'opérateur d'affectation utilisant un type POD (Plain Old Data) dans Rubis.

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 à l'aide de l'opérateur d'affectation. Toute modification apportée à 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 l'exemple précédent, les modifications apportées à une se reflètent dans b, mais pourquoi? En effet, le

instagram viewer
Array l'objet n'est pas un type POD. L'opérateur d'affectation ne fait pas de copie de la valeur, il copie simplement le référence à l'objet Array. le une et b les variables sont maintenant les références au même objet Array, tout changement dans l'une ou l'autre variable sera vu dans l'autre.

Et maintenant, vous pouvez 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 les plus profonds, de sorte que votre copie est appelée «copie superficielle».

Ce que Ruby fournit: dup et clone

Ruby propose deux méthodes pour faire des copies d'objets, dont une qui peut être faite pour effectuer des copies complètes. le Objet # dup fera une copie superficielle d'un objet. Pour y parvenir, le dup appellera la méthode initialize_copy méthode de cette classe. Ce que cela fait exactement dépend de la classe. Dans certaines classes, comme Array, il initialise un nouveau tableau avec les mêmes membres que le tableau d'origine. Cependant, ce n'est pas une copie complète. Considérer ce qui suit.

a = [1,2]
b = a.dup
a << 3
met b.inspect
a = [[1,2]]
b = a.dup
a [0] << 3
met b.inspect

Que s'est-il passé ici? le Tableau # initialize_copy La méthode fera en effet une copie d'un tableau, mais cette copie est elle-même une copie superficielle. Si vous avez d'autres types non POD dans votre baie, utilisez dup ne sera qu'une copie partiellement profonde. Il ne sera aussi profond que le premier tableau, plus profond tableaux, hachages ou d'autres objets ne seront copiés que superficiellement.

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: on s'attend à ce que les objets remplacent cette méthode par une autre qui peut 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 fera 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 tour: Marshalling

«Tacher» 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 qui peut être écrit dans un fichier que vous pouvez «démarsaler» ou «désérialiser» plus tard pour obtenir le même objet. Cela pourrait être exploité pour obtenir une copie complète de n'importe quel objet.

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

Que s'est-il passé ici? Marshal.dump crée un "vidage" 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 entièrement nouveau, avec des éléments de tableau entièrement nouveaux.

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

instagram story viewer