Il est souvent nécessaire de faire une copie d'une valeur dans 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 structure de données avec plusieurs tableaux ou hachages sur le même objet, vous constaterez rapidement qu'il existe de nombreux pièges.
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 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 à 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
une << 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, l'objet Array 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».
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
une << 3
met b.inspect
a = [[1,2]]
b = a.dup
un [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, les tableaux, hachages ou autres objets plus profonds 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 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.
«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 peut être exploité pour obtenir une copie complète de n'importe quel objet.
a = [[1,2]]
b = Marshal.load (Marshal.dump (a))
un [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.