10
votes

Quel est le moyen le plus efficace de copier en profondeur un objet dans Ruby?

Je sais que sérialiser un objet est (à ma connaissance) le seul moyen de copier efficacement un objet (aussi longtemps que ce n'est pas d'état indiquant que io et ce que rien), mais est une bien particulièrement efficace qu'une autre?

Par exemple, car j'utilise des rails, je pourrais toujours utiliser ActiveSupport :: json , to_xml - et de ce que je peux dire que je peux dire que je peux dire que l'objet est l'un des les moyens les plus acceptés de le faire. Je m'attendrais à ce que le marshalling soit probablement le plus efficace de ceux-ci puisqu'il s'agit d'un rubis interne, mais je manque quelque chose?

edit : Notez que sa mise en œuvre est quelque chose que j'ai déjà couvert - je ne veux pas remplacer les méthodes de copie peu profondes existantes (telles que DUP et clone < / Code>), donc je finirai simplement à ajouter probablement objet :: profond_copy , résultat de celui des méthodes ci-dessus (ou des suggestions que vous avez :) qui a le moins de frais généraux.


0 commentaires

3 Réponses :


1
votes

Je pense que vous devez ajouter une méthode initialisée_copy à la classe que vous copiez. Ensuite, mettez la logique pour la copie profonde de là. Ensuite, lorsque vous appelez Clone, il tirera cette méthode. Je ne l'ai pas fait, mais c'est ma compréhension.

Je pense que le plan b serait juste remplacer la méthode du clone: ​​ xxx

sortie xxx

Je suis sûr que vous pourriez faire ce refroidisseur avec un petit bricolage mais pour le meilleur ou pour le pire qui est probablement comment je le ferais.


3 commentaires

Apprécier les commentaires - c'est un moyen de le remplacer, mais il finit par être mis en œuvre serait idéalement discrètement et laissez les méthodes d'origine à la copie peu profonde intacte (par exemple, j'ajouterais simplement objet :: profond_copy ). Avez-vous vu quoi que ce soit à propos de la méthode offrant le moins de frais généraux?


Mis à jour. J'espère que ça aide un peu.


+1, Voir un exemple ici: blog.rubybestpractices.com/posts/ rklemme / ...



23
votes

Je me demandais la même chose, j'ai donc comparé quelques techniques différentes les unes contre les autres. J'étais principalement préoccupé par les matrices et les hachages - je n'ai pas testé d'objets complexes. Peut-être sans surprise, une mise en œuvre personnalisée sur clone s'est révélée être la plus rapide. Si vous recherchez une mise en œuvre rapide et facile, le maréchal semble être le moyen d'aller.

J'ai également comparé une solution XML avec des rails 3.0.7, non indiqué ci-dessous. C'était beaucoup, beaucoup plus lent, ~ 10 secondes pour seulement 1000 itérations (les solutions ci-dessous toutes les 10 000 fois pour la référence). P>

Deux notes concernant ma solution JSON. Tout d'abord, j'ai utilisé la variante C, version 1.4.3. Deuxièmement, cela ne fonctionne pas réellement à 100%, car les symboles seront convertis en cordes. P>

C'était tout géré avec Ruby 1.9.2P180. P>

user       system     total       real
0.230000   0.000000   0.230000 (  0.239257)  (Marshal)
3.240000   0.030000   3.270000 (  3.262255)  (YAML) 
0.590000   0.010000   0.600000 (  0.601693)  (JSON)
0.060000   0.000000   0.060000 (  0.067661)  (Custom)
0.090000   0.010000   0.100000 (  0.097705)  (MessagePack)


4 commentaires

Hey @évan Pon, j'ai ajouté MessagePack dans vos exemples. C'est une bonne option.


MessagePack semble vraiment rapide (2 fois plus rapide que sur mesure sur ma machine). Pourriez-vous mettre à jour la réponse avec la recommandation de l'utiliser au lieu du maréchal?


@Andreybotalov, MessagePack ne gère que quelques classes - essentiellement des chiffres, des cordes, des tableaux et des hachages. Si vous avez d'autres types d'objets, tels qu'un objet de date, cela ne fonctionnera pas pour vous.


J'ai trouvé que si les valeurs que vous êtes en profondeur de clonage sont plus grandes (plus de clés, plus imbriquées, de plus grandes valeurs), le code de rubis personnalisé est plus lent que la messagerie.



0
votes

Probablement la raison pour laquelle Ruby ne contient pas de clonage profond avec la complexité du problème. Voir les notes à la fin.

Pour faire un clone qui "copier en profondeur", des hachages, des tableaux et des valeurs élémentaires, c'est-à-dire une copie de chaque élément dans l'original telle que la copie aura les mêmes valeurs, Mais de nouveaux objets, vous pouvez utiliser ceci: p> xxx pré>

Si vous souhaitez redéfinir le comportement de la méthode code> clone de Ruby's code>, vous pouvez le nommer juste Clone Code> au lieu de DeepcLone Code> (dans 3 places), mais je ne sais pas que la redéfinition de la redéfinition du comportement du clone de Ruby affectera les bibliothèques rubis, ou rubis sur des rails, donc cavalier. Personnellement, je ne peux pas recommander le faire p>

Par exemple:. P>

a = {'a'=>M.new(nil,'g'),'b'=>'y'}               => {"a"=>#<M:0x00000001f8bf78 @z="g">, "b"=>"y"}
b = a.deepclone                                  => {"a"=>#<M:0x00000001766f28 @z="g">, "b"=>"y"}
puts "#{a['a'].object_id} / #{b['a'].object_id}" => 12303600 / 12269460
puts "#{a['b'].object_id} / #{b['b'].object_id}" => 16811400 / 17802280


0 commentaires